pax_global_header00006660000000000000000000000064142510666150014520gustar00rootroot0000000000000052 comment=96cfc398c8d0dd5d172ac9b3d233ee9f89f7effe giada-0.22.0/000077500000000000000000000000001425106661500126465ustar00rootroot00000000000000giada-0.22.0/.clang-format000066400000000000000000000006671425106661500152320ustar00rootroot00000000000000--- 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: ForIndentationgiada-0.22.0/CMakeLists.txt000066400000000000000000000466051425106661500154210ustar00rootroot00000000000000# ------------------------------------------------------------------------------ # Preliminary setup # ------------------------------------------------------------------------------ cmake_minimum_required(VERSION 3.12) # CMAKE_OSX_DEPLOYMENT_TARGET should be set prior to the first project() or # enable_language() command invocation because it may influence configuration # of the toolchain and flags. # Also, see https://stackoverflow.com/questions/34208360/cmake-seems-to-ignore-cmake-osx-deployment-target if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14" CACHE STRING "Minimum OS X deployment version") endif() # ------------------------------------------------------------------------------ # Project # ------------------------------------------------------------------------------ project(giada LANGUAGES CXX) # ------------------------------------------------------------------------------ # Lists definition # # SOURCES - contains the source files # PREPROCESSOR_DEFS - preprocessor definitions # INCLUDE_DIRS - include directories (e.g. -I) # COMPILER_OPTIONS - additional flags for the compiler # LIBRARIES - external dependencies to link # COMPILER_FEATURES - e.g. C++17 # TARGET_PROPERTIES - additional properties for the target 'giada'. # ------------------------------------------------------------------------------ list(APPEND SOURCES src/main.cpp src/mapper.cpp src/core/engine.cpp src/core/worker.cpp src/core/eventDispatcher.cpp src/core/midiDispatcher.cpp src/core/midiMapper.cpp src/core/midiEvent.cpp src/core/quantizer.cpp src/core/conf.cpp src/core/kernelAudio.cpp src/core/jackTransport.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/kernelMidi.cpp src/core/graphics.cpp src/core/patch.cpp src/core/actions/actionRecorder.cpp src/core/actions/actions.cpp src/core/mixer.cpp src/core/synchronizer.cpp src/core/waveManager.cpp src/core/recorder.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/sampleActionRecorder.cpp src/core/channels/midiActionRecorder.cpp src/core/channels/waveReader.cpp src/core/channels/midiController.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/core/channels/midiLearner.cpp src/core/channels/midiSender.cpp src/core/channels/midiReceiver.cpp src/core/channels/channel.cpp src/core/channels/channelShared.cpp src/core/channels/channelManager.cpp src/core/model/sequencer.cpp src/core/model/mixer.cpp src/core/model/recorder.cpp src/core/model/model.cpp src/core/model/storage.cpp src/core/idManager.cpp src/glue/events.cpp src/glue/main.cpp src/glue/io.cpp src/glue/storage.cpp src/glue/channel.cpp src/glue/plugin.cpp src/glue/recorder.cpp src/glue/sampleEditor.cpp src/glue/actionEditor.cpp src/glue/config.cpp src/glue/layout.cpp src/gui/ui.cpp src/gui/dialogs/window.cpp src/gui/dispatcher.cpp src/gui/updater.cpp src/gui/langMapper.cpp src/gui/drawing.cpp src/gui/dialogs/progress.cpp src/gui/dialogs/keyGrabber.cpp src/gui/dialogs/about.cpp src/gui/dialogs/mainWindow.cpp src/gui/dialogs/beatsInput.cpp src/gui/dialogs/warnings.cpp src/gui/dialogs/bpmInput.cpp src/gui/dialogs/channelNameInput.cpp src/gui/dialogs/config.cpp src/gui/dialogs/pluginList.cpp src/gui/dialogs/pluginWindow.cpp src/gui/dialogs/sampleEditor.cpp src/gui/dialogs/pluginWindowGUI.cpp src/gui/dialogs/pluginChooser.cpp src/gui/dialogs/missingAssets.cpp src/gui/dialogs/actionEditor/baseActionEditor.cpp src/gui/dialogs/actionEditor/sampleActionEditor.cpp src/gui/dialogs/actionEditor/midiActionEditor.cpp src/gui/dialogs/browser/browserBase.cpp src/gui/dialogs/browser/browserDir.cpp src/gui/dialogs/browser/browserLoad.cpp src/gui/dialogs/browser/browserSave.cpp src/gui/dialogs/midiIO/midiOutputBase.cpp src/gui/dialogs/midiIO/midiOutputSampleCh.cpp src/gui/dialogs/midiIO/midiOutputMidiCh.cpp src/gui/dialogs/midiIO/midiInputBase.cpp src/gui/dialogs/midiIO/midiInputChannel.cpp src/gui/dialogs/midiIO/midiInputMaster.cpp src/gui/elems/midiIO/midiLearner.cpp src/gui/elems/midiIO/midiLearnerPack.cpp src/gui/elems/fileBrowser.cpp src/gui/elems/soundMeter.cpp src/gui/elems/keyBinder.cpp src/gui/elems/plugin/pluginBrowser.cpp src/gui/elems/plugin/pluginParameter.cpp src/gui/elems/plugin/pluginElement.cpp src/gui/elems/sampleEditor/waveform.cpp src/gui/elems/sampleEditor/waveTools.cpp src/gui/elems/sampleEditor/volumeTool.cpp src/gui/elems/sampleEditor/panTool.cpp src/gui/elems/sampleEditor/pitchTool.cpp src/gui/elems/sampleEditor/rangeTool.cpp src/gui/elems/sampleEditor/shiftTool.cpp src/gui/elems/actionEditor/baseActionEditor.cpp src/gui/elems/actionEditor/baseAction.cpp src/gui/elems/actionEditor/envelopeEditor.cpp src/gui/elems/actionEditor/velocityEditor.cpp src/gui/elems/actionEditor/envelopePoint.cpp src/gui/elems/actionEditor/pianoRoll.cpp src/gui/elems/actionEditor/pianoItem.cpp src/gui/elems/actionEditor/sampleActionEditor.cpp src/gui/elems/actionEditor/sampleAction.cpp src/gui/elems/actionEditor/gridTool.cpp src/gui/elems/actionEditor/splitScroll.cpp src/gui/elems/mainWindow/mainIO.cpp src/gui/elems/mainWindow/mainMenu.cpp src/gui/elems/mainWindow/mainTimer.cpp src/gui/elems/mainWindow/mainTransport.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/mainWindow/keyboard/keyboard.cpp src/gui/elems/mainWindow/keyboard/column.cpp src/gui/elems/mainWindow/keyboard/sampleChannel.cpp src/gui/elems/mainWindow/keyboard/midiChannel.cpp src/gui/elems/mainWindow/keyboard/channel.cpp src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp src/gui/elems/mainWindow/keyboard/midiChannelButton.cpp src/gui/elems/mainWindow/keyboard/midiActivity.cpp src/gui/elems/config/tabMisc.cpp src/gui/elems/config/tabMidi.cpp src/gui/elems/config/tabAudio.cpp src/gui/elems/config/tabBehaviors.cpp src/gui/elems/config/tabPlugins.cpp src/gui/elems/config/tabBindings.cpp src/gui/elems/config/stringMenu.cpp src/gui/elems/basics/scroll.cpp src/gui/elems/basics/pack.cpp src/gui/elems/basics/group.cpp src/gui/elems/basics/scrollPack.cpp src/gui/elems/basics/boxtypes.cpp src/gui/elems/basics/statusButton.cpp src/gui/elems/basics/button.cpp src/gui/elems/basics/resizerBar.cpp src/gui/elems/basics/input.cpp src/gui/elems/basics/liquidScroll.cpp src/gui/elems/basics/choice.cpp src/gui/elems/basics/dial.cpp src/gui/elems/basics/box.cpp src/gui/elems/basics/slider.cpp src/gui/elems/basics/progress.cpp src/gui/elems/basics/check.cpp src/gui/elems/basics/split.cpp src/gui/elems/basics/browser.cpp src/gui/elems/basics/flex.cpp src/gui/elems/basics/tabs.cpp src/utils/log.cpp src/utils/time.cpp src/utils/math.cpp src/utils/gui.cpp src/utils/fs.cpp src/utils/ver.cpp src/utils/string.cpp src/deps/rtaudio/RtAudio.cpp src/deps/mcl-audio-buffer/src/audioBuffer.cpp) list(APPEND PREPROCESSOR_DEFS) list(APPEND INCLUDE_DIRS ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src) list(APPEND COMPILER_OPTIONS) list(APPEND LIBRARIES) list(APPEND COMPILER_FEATURES cxx_std_17) list(APPEND TARGET_PROPERTIES) # ------------------------------------------------------------------------------ # Detect OS # ------------------------------------------------------------------------------ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(OS_LINUX 1) elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") set(OS_WINDOWS 1) elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(OS_MACOS 1) elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") set(OS_FREEBSD 1) else() message(FATAL_ERROR "Unsupported platform '${CMAKE_SYSTEM_NAME}', quitting.") endif() # ------------------------------------------------------------------------------ # Compiler warnings # ------------------------------------------------------------------------------ if(DEFINED OS_WINDOWS) list(APPEND COMPILER_OPTIONS /W4 /bigobj /external:anglebrackets /external:W0) else() list(APPEND COMPILER_OPTIONS -Wall -Wextra -Wpedantic) endif() # ------------------------------------------------------------------------------ # Options # ------------------------------------------------------------------------------ option(WITH_VST2 "Enable VST2 support." OFF) 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 TEST_RESOURCES_DIR="${CMAKE_SOURCE_DIR}/tests/resources/") endif() if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") list(APPEND PREPROCESSOR_DEFS NDEBUG) endif() # ------------------------------------------------------------------------------ # Dependencies # ------------------------------------------------------------------------------ # Threads (system) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) list(APPEND LIBRARIES Threads::Threads) # 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) 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}) if (NOT LIBRARY_RTMIDI) message(FATAL_ERROR "Can't find RtMidi, aborting.") endif() message("RtMidi library found in " ${RtMidi_DIR}) endif() # RtMidi header path may vary across OSes, so a fix is needed. # TODO - Find a way to avoid this additional step find_path(LIBRARY_RTMIDI_INCLUDE_DIR RtMidi.h PATH_SUFFIXES rtmidi) list(APPEND INCLUDE_DIRS ${LIBRARY_RTMIDI_INCLUDE_DIR}) # FLTK set(FLTK_SKIP_FLUID TRUE) # Don't search for FLTK's fluid set(FLTK_SKIP_OPENGL TRUE) # Don't search for FLTK's OpenGL find_package(FLTK REQUIRED NO_MODULE) list(APPEND LIBRARIES fltk) list(APPEND INCLUDE_DIRS ${FLTK_INCLUDE_DIRS}) message("FLTK library found in " ${FLTK_DIR}) # Libsndfile find_package(SndFile CONFIG) if (SndFile_FOUND) list(APPEND LIBRARIES SndFile::sndfile) message("Libsndfile library found in " ${SndFile_DIR}) 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) if (NOT LIBRARY_SNDFILE) message(FATAL_ERROR "Can't find libsndfile, aborting.") endif() list(APPEND LIBRARIES ${LIBRARY_SNDFILE}) message("Libsndfile library found in " ${LIBRARY_SNDFILE}) # Find optional dependencies. find_library(LIBRARY_FLAC NAMES flac FLAC) find_library(LIBRARY_OGG NAMES ogg) find_library(LIBRARY_OPUS NAMES opus libopus) find_library(LIBRARY_VORBIS NAMES vorbis) find_library(LIBRARY_VORBISENC NAMES vorbisenc) if(LIBRARY_FLAC) list(APPEND LIBRARIES ${LIBRARY_FLAC}) endif() if(LIBRARY_OGG) list(APPEND LIBRARIES ${LIBRARY_OGG}) endif() if(LIBRARY_OPUS) list(APPEND LIBRARIES ${LIBRARY_OPUS}) endif() if(LIBRARY_VORBIS) list(APPEND LIBRARIES ${LIBRARY_VORBIS}) endif() if(LIBRARY_VORBISENC) list(APPEND LIBRARIES ${LIBRARY_VORBISENC}) endif() endif() # Libsamplerate find_package(SampleRate CONFIG) if (SampleRate_FOUND) list(APPEND LIBRARIES SampleRate::samplerate) message("Libsamplerate 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() # fmt find_package(fmt REQUIRED) list(APPEND LIBRARIES fmt::fmt) # nlohmann_json (embedded as git submodule) set(JSON_Install OFF CACHE INTERNAL "") # No need to install it set(JSON_BuildTests OFF CACHE INTERNAL "") # Don't build tests add_subdirectory(src/deps/json) list(APPEND LIBRARIES nlohmann_json::nlohmann_json) # Catch (if tests enabled) if (WITH_TESTS) find_package(Catch2 CONFIG REQUIRED) list(APPEND LIBRARIES Catch2::Catch2) message("Catch2 library found in " ${Catch2_DIR}) endif() # ------------------------------------------------------------------------------ # Conditional checks for different platforms. # ------------------------------------------------------------------------------ if(DEFINED OS_LINUX) find_package(X11 REQUIRED) find_package(ALSA REQUIRED) find_library(LIBRARY_PULSE NAMES pulse REQUIRED) find_library(LIBRARY_PULSE_SIMPLE NAMES pulse-simple REQUIRED) find_library(LIBRARY_FONTCONFIG NAMES fontconfig 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} ${JACK_LDFLAGS} ${CMAKE_DL_LIBS} ${ALSA_LIBRARIES} pthread stdc++fs) 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) list(APPEND LIBRARIES dsound) list(APPEND SOURCES src/deps/rtaudio/include/asio.h src/deps/rtaudio/include/asio.cpp src/deps/rtaudio/include/asiosys.h 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 src/ext/resource.rc) list(APPEND INCLUDE_DIRS src/deps/rtaudio/include) list(APPEND PREPROCESSOR_DEFS __WINDOWS_ASIO__ __WINDOWS_WASAPI__ __WINDOWS_DS__) elseif(DEFINED OS_MACOS) find_library(CORE_AUDIO_LIBRARY CoreAudio REQUIRED) find_library(CORE_MIDI_LIBRARY CoreMIDI REQUIRED) find_library(COCOA_LIBRARY Cocoa REQUIRED) find_library(CARBON_LIBRARY Carbon REQUIRED) find_library(CORE_FOUNDATION_LIBRARY CoreFoundation REQUIRED) find_library(ACCELERATE_LIBRARY Accelerate REQUIRED) find_library(WEBKIT_LIBRARY WebKit REQUIRED) find_library(QUARZ_CORE_LIBRARY QuartzCore REQUIRED) find_library(IOKIT_LIBRARY IOKit REQUIRED) list(APPEND LIBRARIES ${CORE_AUDIO_LIBRARY} ${CORE_MIDI_LIBRARY} ${COCOA_LIBRARY} ${CARBON_LIBRARY} ${CORE_FOUNDATION_LIBRARY} ${ACCELERATE_LIBRARY} ${WEBKIT_LIBRARY} ${QUARZ_CORE_LIBRARY} ${IOKIT_LIBRARY}) list(APPEND SOURCES src/utils/cocoa.mm src/utils/cocoa.h) # TODO: why?? list(APPEND INCLUDE_DIRS "/usr/local/include") list(APPEND PREPROCESSOR_DEFS __MACOSX_CORE__) elseif (DEFINED OS_FREEBSD) find_package(X11 REQUIRED) 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) 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} ${CMAKE_DL_LIBS} pthread) list(APPEND PREPROCESSOR_DEFS WITH_AUDIO_JACK __LINUX_PULSE__ __UNIX_JACK__) endif() # ------------------------------------------------------------------------------ # Extra parameters if compiled with VST. # ------------------------------------------------------------------------------ if(WITH_VST2 OR WITH_VST3) list(APPEND SOURCES 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) list(APPEND INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/deps/juce/modules ${CMAKE_SOURCE_DIR}/src/deps/vst3sdk) if(DEFINED OS_LINUX) find_package(Freetype REQUIRED) list(APPEND LIBRARIES ${FREETYPE_LIBRARIES}) list(APPEND INCLUDE_DIRS ${FREETYPE_INCLUDE_DIRS}) endif() list(APPEND PREPROCESSOR_DEFS WITH_VST JUCE_DEBUG=$> JUCE_MODAL_LOOPS_PERMITTED=1 JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED=1 JUCE_MODULE_AVAILABLE_juce_gui_basics=1 JUCE_STANDALONE_APPLICATION=1 JUCE_PLUGINHOST_AU=0 JUCE_WEB_BROWSER=0 JUCE_USE_CURL=0) if(WITH_VST2) list(APPEND PREPROCESSOR_DEFS WITH_VST2 JUCE_PLUGINHOST_VST=1) endif() if(WITH_VST3) list(APPEND PREPROCESSOR_DEFS WITH_VST3 JUCE_PLUGINHOST_VST3=1) endif() endif() # ------------------------------------------------------------------------------ # Finalize 'giada' target (main executable). # ------------------------------------------------------------------------------ add_executable(giada) target_compile_features(giada PRIVATE ${COMPILER_FEATURES}) target_sources(giada PRIVATE ${SOURCES}) target_compile_definitions(giada PRIVATE ${PREPROCESSOR_DEFS}) target_include_directories(giada PRIVATE ${INCLUDE_DIRS}) target_link_libraries(giada PRIVATE ${LIBRARIES}) target_compile_options(giada PRIVATE ${COMPILER_OPTIONS}) # ------------------------------------------------------------------------------ # Install rules # ------------------------------------------------------------------------------ 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 # ------------------------------------------------------------------------------ # 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$<$:Debug>") 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() giada-0.22.0/COPYING000066400000000000000000001045131425106661500137050ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program 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. This program 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 this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . giada-0.22.0/ChangeLog000066400000000000000000001470601425106661500144300ustar00rootroot00000000000000-------------------------------------------------------------------------------- Giada - Your Hardcore Loopmachine. Developed by Monocasual Laboratories www.giadamusic.com CHANGELOG -------------------------------------------------------------------------------- 0.22.0 --- 2022 . 06 . 13 - Multi-language support via langmaps (#34) - Add new 'fmt' dependency - Add vcpkg.json manifest file - Log compiled RtMidi APIs on startup - Fix missing header with RtMidi 5.0.0 - Fix Stream Linker button not working (#585) - Minor code refactoring and cleanups 0.21.0 --- 2022 . 04 . 01 - Custom keyboard mappings for global actions (#213) - Pressing 'Esc' key no longer closes windows - Resizable Configuration window - Shut down the main UI nicely when closing a project or loading a new one - Prevent crashes when loading a new project by cleaning up the data model - Fix wrong plug-in processing where the local plug-in buffer was incorrectly deleted in case of instruments (#563) - Lots of UI code modernizations and cleanups - [Windows] Fix several assertions and MSVC warnings 0.20.1 --- 2022 . 02 . 21 - New MIDI I/O activity LEDs on channels (#143) - New "Missing Assets" alert window (#344) - Many smaller improvements and cleanups in UI code - Add ability to sort installed plug-ins by Format (VST, VST3, ...) - Update JUCE to 6.1.5 - Update custom RtAudio submodule (now pointing to 6.0.0beta1) - Optimize internal buffer Giada <-> JUCE conversion - Remove old plug-in parameter storage used in old patches - Fix deadlock when using JACK transport - Fix Action Editor grid refresh when changing BPM while the editor window is open (#547) - Fix plug-in clone operation while cloning a channel (#551) 0.20.0 --- 2022 . 01 . 24 - Show progress bar for long operations - Improved rendering algorithm for sample channels - Fix wrong sample tail rendering when pitch != 1.0 - Always display play head in Action Editor (fix #534) - Fix re-initialization order of engine sub-components (fixes #533) - Change 'kill chan' wording to 'stop note' in Action Editor (fixes #532) - Update solo count when deleting a channel (fixes #540) - Update Main Window title saving a new project (fixes #541) - [Config] Don't skip MIDI device fetching if one of the ports fail to open - [CMake] Include FLTK as suggested in the official docs - Add more unit tests for some Channel components - Minor cleanups and refactoring 0.19.2 --- 2021 . 12 . 16 - Fix wrong computation of soloed channels 0.19.1 --- 2021 . 12 . 15 - Enable JUCE_DEBUG in Debug builds - New MidiLighter tests + compile-time dependency injection - Set limits to minimum zoom level in Action Editors (#425) - Refactoring and code cleanup for Channel class and other sub-components - Update JUCE to version 6.1.2 - Update RtAudio to version 5.2.0 - Sanitize MIDI ports values (fixes #515) - MidiLighter improvements and cleanups (fixes #517) - Fix off-the-beat metronome (#522) - Fix number of plug-ins found not being updated after a scan (fix #523) - Fix PluginManager initialization - Fix pthread linking in CMake (#520) - Fix build info not being printed correctly on startup - [Linux] Fix X error messages on closing some plug-in editors - [Linux] Fix wrong icon file in XDG desktop file 0.19.0 --- 2021 . 11 . 01 - New "One-shot Pause" channel mode - Refactoring: new component-based architecture - Fix crash on startup if recording from mono input - Improved event handling for plug-ins GUIs - Fix many compiler warnings on menu items initialization 0.18.2 --- 2021 . 09 . 13 - New stereo In/Out audio meters - Revamped Action Editor: better UI, improved usability - Show play head in Action Editor - Implement queue for MIDI events, fix issue #482 - Simplified Event Dispatcher's Event type - Move JACK transport operations to new JackTransport class - Always pick sample rate from the first audio device when using JACK - Don't send MIDI events if MIDI channel is not playing (#499) or muted (#497) - Add AtomicSwapper as git submodule - Upgrade JUCE to version 6.1.0 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 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) - Fix wrong action ID mapping when cloning a channel (#426) - Fix scrambled MIDI bindings (#427) 0.17.0 --- 2020 . 11 . 15 - Add CMake build system - VST3 support - Show descriptive plug-in names in Plug-in List Window - Resizable plug-in list - New persistence mechanism for Plug-ins state - Improved text truncation for small buttons and text boxes - Beautify Sample Editor window - Resizable plug-in list window - Show descriptive plug-in name in plug-in list - Update JUCE, version 6.0.4 - Update Catch2 to version 2.13.2 - Replace old filesystem functions in fs.h with std::filesystem - Add VST3 SDK as git submodule - Set minimum macOS version to 10.14 - Statically link the MSVC runtime library on Windows - Avoid crash on opening plug-in list with invalid plug-ins - Rewind sample channels in loop.once.bar mode on bar, if still playing (fix #403) - Modernize log::print() function to handle std::string arguments (PR #402) - Fix playStatus logic for ending sample channels in loop-once-bar mode (#404) - Fix shrinking beats that could glitch the output (#361) 0.16.4 --- 2020 . 09. 19 - Support for mono inputs - Overdub mode for Sample Channels with optional overdub protection - Disable record-on-signal mode when sequencer is running - Shift + [click on R button] kills action reading when "Treat one-shot channels with actions as loops" option is on - Start MIDI channels automatically after action recording session - Fix wrong sample rate conversion when project rate != system rate - Fix Wrong begin/end sample markers when loading a project with samplerate != system.samplerate - Fix wrong MIDI learn mapping for master parameters - Fix BPM button disabled after audio recording session 0.16.3 --- 2020 . 06. 15 - Non-virtual Channels architecture - Added G_DEBUG macro - Optimized CPU usage when playing with many channels - Increased UI refresh rate to 30 frames per second - Improved quantizer precision - Simplified behavior when halting channels containing recorded actions - Fix wrong audio sample looping with pitch != 1.0 - Fix MIDI input master values not stored on quit - Fix One-shot press channel mode not working via mouse - Fix Action recording overlap (both live and via Action Editor) - Fix crash when loading a project with missing audio files - Fix BPM not changing via Jack 0.16.2 --- 2020 . 02 . 18 - Switch to Json for modern C++ library for reading and writing Json data - Resizable channels, improved version - Drop support for raw patches (still readable for backward compatibility) - Simplify global configuration parameters - Simplify column data storage in patch files - Center all micro-subwindows to screen - Revamped MIDI learning algorithm and related UI components - Always display 'R' button in Sample Channel - Don't download external files for unit tests - Optimized UI drawings for base buttons - Move build info from 'About' window to console log - Update RtAudio to 5.1.0 - Fix crash during audio recording after opening a project (thanks AdTb!) 0.16.1 --- 2020 . 01 . 08 - FreeBSD support - Ability to remove empty columns manually - Gray out bpm value when in JACK client mode - 'Reset to init state' becomes 'close project' under File menu - [Linux] Upgrade Travis CI Linux machine to Xenial - Add namespaces to file system and logging functions - Remove unused G_quit global variable - Fix Sample Channels in loop mode not playing automatically after audio recording - Fix action recording button status during audio recording, signal mode 0.16.0 --- 2019 . 12 . 02 - Fix columns' resizer bar height on vertical window resize - Fix crash on MIDI learn global commands - Fix wrong channel routing when triggering MIDI learnt commands - Fix rewind button not rewinding sample channels in LOOP_* mode - Use actual buffer size from KernelAudio when loading channels from a patch - Remove FLTK multithreading initialization 0.16.0 beta-2 --- 2019 . 11 . 11 - Remove all pthread.h leftovers - Fix Windows build - Fix memory corruption on Keyboard refresh - Fix wave size corruption while editing samples in Sample Editor - Fix freeze when cloning a Sample Channel with a sample in it - Fix buffer overflow when playing an edited sample - Fix crash when loading a project with missing plug-ins - Fix freeze when pressing 'play' during an audio recording session - Fix play/ending UI status of MIDI channels - Fix plug-in sorting on reload - Fix crash when reloading a sample in the Sample Editor - Fix messy 'R' button status when toggled - Fix missing icons and broken checkboxes - Optimize model updates on keyboard interaction - Always read Columns data from patch files - Show missing (and removable) plug-ins in Plug-in Window list - Create default empty columns on 'Reset to initial state' - Save relative Wave paths in project files 0.16.0 beta-1 --- 2019 . 10 . 19 - Fix macOS build error + warnings 0.16.0 beta-0 --- 2019 . 10 . 19 - New internal engine<->UI architecture - New persistence layer - New MIDI queue for incoming live MIDI messages - Switch to std::thread - Absolute #include paths in source code - Removed Boost parameter from Sample Channel 0.15.4 --- 2019 . 03 . 22 - New record-on-signal option for input and action recording - Initial support for plug-ins with mono I/O buses - PluginHost refactoring - Smart pointers for Wave and Plugin objects - Remove old and deprecated input delay compensation - Optimized audio IO processing in Mixer callback - Atomic I/O meters with improved accuracy - Fix memory leak when replacing samples in a Sample Channel - Fix plug-ins ordering method when re-opening Giada - Fix silent Sample Channel when recording actions a second time - Fix velocity always discarded when sending key-press to Sample Channel - Fix inability to record actions with quantizer enabled 0.15.3 --- 2018 . 12 . 24 - Action recorder refactoring - Optional midimap parameters (thank you @tomek-szczesny) - Support for "inaudible" MIDI lightning events (thank you @tomek-szczesny) - Build AppImage for Linux on Travis CI instance - Huge optimization of the AppImage binary file - Fix Action Editor repaint on min/max zoom levels - "Resize recording" flag has been removed - Change text labels for channel operations - Smarter column assignment while loading a patch/project - Fix wrong resizer bar width between Action Editor widgets when zooming - Can't display custom channel name in Sample Channel (fixed) - Fix crash when cloning Sample Channel with audio data in it - Clone channel doesn't clone channel name (fix #219) 0.15.2 --- 2018 . 09 . 05 - New sample-accurate Action Editor - New MIDI Velocity Editor widget - Ability to move MIDI events vertically in piano roll (i.e. change note) - Remove mute action recording - Better handling of MIDI devices that send NOTEON + velocity 0 as NOTEOFF - Avoid calls to deprecated JUCE plug-ins methods - Removed useless pthreadGC2.dll from Windows package - Can't kill MIDI channels (fix #197) - Can't record MIDI actions (fix #202) - Fix missing first beat on metronome rendering - Fix crash on opening plug-in window on macOS 0.15.1 --- 2018 . 07 . 03 - Deep code refactoring, featuring Channels processors - Many new unit tests added - Simplify mutex mechanism - Fix wrong quantizer value on patch/project load - Remove the old, buggy and glitchy internal crossfade algorithm - Fix many potential plug-in crashes on Linux - Properly close plug-in window on plug-in removal - Improve BPM changes while running as JACK client 0.15.0 --- 2018 . 04 . 18 - Refactor audio engine into frame-based processing - Refactor channels readers/writers into channelManager namespace - Smarter Solo algorithm - Fix missing .wav extension on recorded audio takes - Fix wrong Channel status update after 'Clear all actions' 0.14.6 --- 2018 . 03 . 15 - MIDI velocity drives volume for one-shot sample channels - FLAC and Ogg support - Ability to use system-provided Catch library (GitHub #151) - Update Catch to version 2 - Fix unreadable tabs title in Configuration Window (GitHub #168) - Fix crash on opening About window - Fix 'read actions' button behavior during ending and waiting statuses - Fix sound card initialization on MacOS - [Windows] Fix UI stuck on top-right corner - [Windows] Fix browsing for directories 0.14.5 --- 2018 . 01 . 15 - OS X builds on Travis CI - AppImage executable for Linux - Support for multiple plug-in directories - New directory browser for adding plug-in directories - Update plug-in's parameters on program change in plug-in's window - Improved MIDI action management in Piano Roll - Simplified conditional rules in Makefile.am - Fix crash on MIDI learn for plug-in parameters - Fix crash in MIDI input window if MIDI in params are 0 - Fix unwanted new action when dragging piano items in Piano Roll - Fix crash while recording on existing project (GitHub #161) - Fix crash on startup in Windows build 0.14.4 --- 2017 . 10 . 28 - Renameable channels - Portable VST path - [Sample Editor] Sample shift tool - [Linux/Mac] Don't skip '/' path when navigating to upper folders - Ability to process more than one plug-in instrument at once - Beautify Configuration Window - Bring VST window to front when opening UI - Save 'arm' status to patch/project file - Revamped Beats and Bpm input windows - Simplified audio samples' storage in project folders - Update JUCE to version 5.1.2 - UI-less plug-in window refinements - Update UI-less plug-in window on MIDI parameter's change - Strip .gptc/.gprj extension from patch name - [Sample Editor] Fix non-working 'cut' operation - Fix missed MIDI events with more than 1 plug-in in the stack - Fix File Browser path widget drawn incorrectly in OS X - Fix missing MIDI learn for 'Arm channel' and 'Kill channel' 0.14.3 --- 2017 . 09 . 18 - [Sample Editor] New "reverse selection" function - [Sample Editor] New "normalize hard" function - [Sample Editor] New "copy to channel" function - [Sample Editor] New "copy & paste" function - [Sample Editor] Double click on waveform selects all - [Sample Editor] Fix garbled characters in window's title - [Sample Editor] Fix wrong result on "set pitch to song/bar" - Resizable channels - Remove calls to malloc/free in Mixer (use new/delete instead) - Improved UI management of VST plugins - Fix infinite loop for one shot retrig samples with quantizer > 0 - Fix wrong geChannel count while saving a patch - Fix missing greyed-out options in Sample Channel's menu when loading a wrong sample - Fix crash while audio recording with BPM set below the default 120 - Print correct octave numbers in Piano Roll 0.14.2 --- 2017 . 08 . 14 - [Sample Editor] Audible preview (with optional loop mode) - [Sample Editor] Frame-precise editing - [Sample Editor] Show sample's information - [Sample Editor] Improved fade out algorithm - [Sample Editor] Process both left and right channel's data while drawing - Better Wave objects handling - Improved channels' memory management - Improved empty columns cleanup algorithm - Update Catch version - Update JUCE version (5.1.1) - Update Jansson version (2.10) - Fix missing tempo update on reset to init state - Fix wrong memory allocation for UI-less plugins 0.14.1 --- 2017 . 07 . 16 - Update JUCE library to 5.0.2 - Show play head in Sample Editor - Refactor pop up menu in Sample Editor - Many small fixes and optimizations in waveform drawing routine - Makefile cleanup - Fix crash while recording with beats/bars greater than 4/1 (GitHub #134) 0.14.0 --- 2017 . 05 . 29 - Sample Editor reorganized and refactored - Removed support for old ini-based patch files - Improved and simplified pan algorithm - Ability to toggle input monitoring while recording audio - Lots of code refactoring - Convert all .h headers to C++ headers - Update Libsndfile to version 1.0.28 - Fix crash when recording audio - Fix wrong file path when exporting samples - Fix a bug that prevented begin/end handles to work in Sample Editor - Fix Sample Editor's grid value not being stored properly on close 0.13.4 --- 2017 . 04 . 23 - Removed support for old ini-based MIDImap files - Initial support for channel-based MIDI filtering - New Orphaned MIDI events in Piano Roll editor - Improve action filtering in Piano Roll editor - Lots of code refactoring - New test suite for Action Recorder - Fix obscure bug when overdubbing actions and a null loop occurs - Fix "clear all actions" menu refresh when removing items on Piano Roll 0.13.3 --- 2017 . 03 . 25 - Strip VST folder from Git repository - Fix 'Close' button's position inside MIDI input window - Update RtMidi to version 2.1.1 - Improve 'free channel' function (GitHub #105) - New 'Clock' structure for timing operations - New Jack implementation with BPM sync and Rewind (GitHub #89) - Fix missing tracker reset on 'free channel' function (GitHub #99) 0.13.2 --- 2017 . 01 . 14 - MIDI learn for plugins parameters - Toggle hidden files in File Browser - Fix broken compilation when build without VST support - Make sure PluginChooser window has a sane size - Decouple Recorder from any global variable - Better source code organization - Make plugin creation more robust - More source code reorganization - Fix crash on clicking scrollbar arrows (GitHub #53) - Fix crash when doubling/dividing length while recording (GitHub #110) 0.13.1 --- 2016 . 11 . 16 - Input MIDI to MIDI channels/plugins - Refinements to show/hide 'R' button's dynamics - Increase piano roll items' height - Set input volume to max by default - Start live-recorded sample channels right away - Avoid potential crashes when loading samples on running channels - Generate metronome during output post-processing - Better widgets' layout in Sample Editor - Lots of source code optimizations and cleanups - Fix inverted 'R' button's status (GitHub #94) - Better handling of 'R' button's status when the sequencer is off (GitHub #95) - Fix non-playing samples if live-recorded and 'R' button is on (GitHub #93) - Reset button statuses once channels have been freed (GitHub #100) - Fix missing ASIO and WASAPI APIs on Windows (GitHub #96) - Missing RtMidi libs on Linux (GitHub #102) - Fix fade-in/fade-out editing not triggering alert on save (GitHub #101) 0.13.0 --- 2016 . 09 . 20 - Deep file browser refactoring - Save browser's scroll position and last item selected on opening - Load patches/projects/samples on double click - 64 bit builds for Windows - Prevent deprecated patch from crashing if a plugin is not found in the stack - Force logger to flush to file on Windows - Add more default values for windows' dimensions and positions - Avoid crashes on Configuration panel if no midimaps were selected - Fix missing keyRelease actions in action editor - Update JUCE to version 4.2.3 - Don't include JUCE on tests without VST support (GitHub #75) - Fix compilation errors on GCC 6 (GitHub #82) - Fix includes on OSX (GitHub #92) - Fix wrong channel's actions count that prevented "R" button to be toggled properly - Fixed a bug that prevented actions on frame 0 to being properly reproduced - Make Recorder a proper class - Better naming convention for ActionEditor's children classes - Source code reorganization 0.12.2 --- 2016 . 06 . 02 - Update RtAudio to version 4.1.2 - Add WASAPI support on Windows - Sortable plugins list - Simplify custom RtAudio build and inclusion on Linux - Fix crashes on startup on OS X El Capitan - Store position and size of Available Plugins window - Untangle Channels' code from global variables 0.12.1 --- 2016 . 05 . 06 - Show percentage progress for plugin scan - Notify if plugins are missing - Notify if unknown plugins are present - Fix potential segfault on MasterIn/MasterOut plugins loading - Proper cleanup of JUCE resources - Internal refactoring on PluginHost's global variables 0.12.0 --- 2016 . 03 . 07 - Port to JUCE Framework for audio plugin management - Increase global font size - Minor UI fixes and cleanups - Add ability to run tests outside Travis CI - Switch to C++11 - 64 bit binaries for OS X - Use new constant for global font size 0.11.2 --- 2016 . 01 . 16 - New JSON-based midimap files - Add new channel by right-clicking anywhere on a column - Show warning if patch is using the deprecated file format - Do not force 32 bit compilation on OS X - Fix warnings and errors on GCC 5.3 - Fix a bug that prevented MIDI Jack from being selected on Linux 0.11.1 --- 2015 . 12 . 22 - Ability to clone channels - New JSON-based configuration file - Port all vectors from old gVector to std::vector - Deactivate all other MIDI fields when changing MIDI system in Config window - Minor optimizations in configuration panel, Audio tab - Assume 'none' as default sound system - Include Catch header file in source package - Update Travis CI environment to Ubuntu Trusty - Fix missing sanitization after reading configuration file - Fix garbage text in device info window - Fix wrong config value if no midimaps are available - Fix garbage text while printing device and port names 0.11.0 --- 2015 . 12 . 02 - New JSON-based patch system - Properly store column width in patch - Port all const char* strings to std::string in patch/project glue layer - Switch to SemVer-like internal versioning system - More source code reorganization - Fix potential memory leaks in Mixer - Fix missing static link of RtMidi on Linux - Unable to store pitch values > 2.0 (fixed) - Missing assigned key after opening patch (fixed) 0.10.2 --- 2015 . 10 . 21 - Setup Travis CI automated builds - Add base framework for unit testing (with Catch) - Improve behavior of Loop Once family when the sequencer is halted - Fix empty sample path in sample channels when saving a Project - Fix disabled "edit actions" for sample channels - Fix missing pthreadGC2.dll in Windows build 0.10.1 --- 2015 . 08 . 26 - Massive source folders refactoring - Improved usability of "play" buttons for channels - Remove support for patches created with Giada < 0.6.x - Fix check for configured soundsystem (would break compilation on g++5) - Small fixes and cleanup in Makefile.am 0.10.0 --- 2015 . 07 . 05 - MIDI lightning output - Other minor fixes 0.9.6 --- 2015 . 05 . 11 - Keyboard binding for MIDI channels - Support for multiple files in drag-n-drop operations - Different color for wait/end statuses - Small improvements to Keyboard grabber widget - Fix random crashes with Jack enabled - Fix weird behavior with multiple drag and drop - Code refactoring 0.9.5 --- 2015 . 03 . 28 - Better column resize algorithm - New patch loading system with permanent MIDI mapping - Ability to clear assigned keys (keyboard mode) - Improved zoom icons in editors - Fix deprecation warning in configure.ac 0.9.4 --- 2015 . 02 . 24 - Drag-n-drop now works also in existing channels - Store 'resize recordings' flag in giada.conf - Better management of duplicate samples - Add more VST debug information - Minor fixes and tweaks 0.9.3 --- 2015 . 02 . 01 - New GUI improvement: responsive and resizable columns - Upgrade to FLTK 1.3.3 - More robust column handling mechanism - Support for MIDI devices without note-off message (@blablack) - Fix segfaults when saving a patch with missing plugins - Fix many minor graphical bugs - Fix wrong vector assignment in MIDI send event - Fix reloaded patches with no right tempo/beats displayed - Fix random odd frames when adding/moving events in Piano Roll - Minor internal cleanup 0.9.2 --- 2014 . 11 . 29 - New grid layout in Sample Editor - Load samples via drag n drop - Add new utility functions: gTrim and gStripFileUrl - Fix "normalize" button position in Sample Editor - Minor waveform drawing optimizations - Add missing files for RtAudio-mod compilation - All one-shot mode, if fired manually, get the first frame truncated (fixed) 0.9.1 --- 2014 . 09 . 24 - Bring back custom version of rtAudio in source package - Automatically turn up volume when adding new channel - Updated 'misc' tab in configuration panel - Fix startup crash on OS X - Fix missing jack headers 0.9.0 --- 2014 . 08 . 18 - New full-screen GUI - Multi-column support - Advanced logging system - Upgrade to RtAudio 4.1.1 and RtMidi 2.1.0 - Removed embedded RtAudio (thanks to Arty) - Fix wrong processing of VST MIDI events on 64 bit version - Fix stretched buttons when resizing sample editor window - "Clear all samples" destroys channels (fixed) - "Free channel" messes up loop / mute buttons (fixes) - Fix potential recordings with odd frames 0.8.4 --- 2014 . 03 . 27 - New mode 'Loop Bar Once' - Several small improvements and cleanups to internal utils functions - Fixed missing title in several subwindows - (win) Fix runtime error when loading a new project - Fix chan reset when clicking on waveform - Properly close subwindows after a channel has been deleted - Fix 'reload' button not working for samples with updated names 0.8.3 --- 2014 . 02 . 14 - Experimental MIDI timing output with MTC and MIDI clock - Expose Sequencer x2 and /2 via MIDI - New pitch operators x2 and /2 - Internal xfade process restored - "set key..." becomes "setup keyboard input" for sample channels - MIDI events are now saved as unsigned int in patch - Same expression on both sides of '|' in recorder.cpp (fixed) - Muted channels leak some glitches on 'kill' event (fixed) - Piano roll can't be edited anymore if beats == 32 (fixed) - Noise when adding new MIDI channel (fixed) - Boost and Normalize not working (fixed) - Multiple copies of every file used by the patch (fixed) - Samples with -1, -2, ... -n suffix are not included in patch (fixed) - Segfaults when quantizing samples (fixed) 0.8.2 --- 2014 . 01 . 13 - Pitch control exposed via MIDI - New tools in Sample Editor (linear fade in/out, smooth edges) - Implemented vstEvent->deltaFrames, gaining more precision with vst MIDI events - Add Fl::lock/Fl::unlock dynamics to glue_ calls where needed - Avoid pitch sliding when changing pitch of a sample in status OFF - Update copyright info in source files - Internal fade in and fade out restored - Add 'Giada' keyword to desktop file - Fix annoying glitches when playing very short samples - Fix random crashes when controlling giada via MIDI - Fix missing MIDI mapping for read-actions button 0.8.1 --- 2013 . 12 . 09 - New, high-quality pitch control based on libsamplerate - New set of functions 'spread sample to beat/song' [known issues] - Internal crossfades have been temporarily disabled. Some clicks may occur 0.8.0 --- 2013 . 11 . 03 - Initial MIDI input support - Fix freeze when recording audio inputs on a second channel - Fix 'R' button to show up even if the channel has no actions - Fix weird drawings of keypress actions in action editor - Free channel: delete 'R' button as well - Shift+key does not kill loop mode channels in a wait status - Fix issue with 'R' button and newly added actions - Remove "left"/"right" labels from main buttons 0.7.3 --- 2013 . 09 . 14 - Experimental 64 bit compilation (Linux only) - Massive internal cleanup of channel/gui channel layers - Set default mode to full volume on sample load - Set default mode to oneshot basic - Faster drawings in piano roll - Visual aids in piano roll - Scroll to pointer in piano roll - Several minor improvements in piano roll's usability - Revised VST Carbon window popup system - Minor improvements in startInputRec/stopInputRec procedure - Fix compile error using local type Plugin* in Channel's constructor - Fix segfault in OSX when working with VST windows 0.7.2 --- 2013 . 07 . 27 - Initial MIDI output support - Mute now affects channels with VSTi signals - Lots of deb package improvements - Complete rewrite of VST GUI part on OS X - Don't send MIDI mute on sample channels - Send MIDI mute for MIDI channels in play mode - Fix wrong looping due to VST processing in mixer::masterPlay - Fix jack crashes when using Giada with ALSA - Fix VST random crashes on OSX, bus error - Fix input device set to -1 after a system change 0.7.1 --- 2013 . 06 . 27 - Initial Jack Transport support - Send global note off when sequencer is being stopped - Send note off when deleting notes in Piano Roll - Store position and size of Piano Roll in conf file - Avoid overlap MIDI notes in Piano Roll - MIDI channel refactoring - MIDI channels now behave like loop-mode ones - Fix graphical bugs in Action Editor, sample mode - Fix refresh issue in Piano Roll when deleting items - Lots of invisible cleanups and improvements 0.7.0 --- 2013 . 06 . 05 - Initial MIDI output implementation - Initial VSTi (instrument) support - New piano roll widget in action editor - New chan mode: MIDI vs SAMPLE - Fix E-MU Tracker Pre not correctly listed in audio in/output 0.6.4 --- 2013 . 05 . 07 - Resizable plugin parameter window - New and standard package name format -. - Implement RtAudio::getCompiledApi() to fetch compiled APIs - Implement audioMasterGetSampleRate, audioMasterGetLanguage VST opcodes - Add drop-down menu for buffer size values in config panel - Enhance project portability between OSes - Lots of fixes and improvements for VST strings and parameters - Avoid segfault when loading recs from a patch with files not found - Always remember selected program when shifting up/down plugins - Fix wrong size of single_press displayed in action editor - Fix volume actions resized with value set to zero - Fix volume envelope always over the cover area - Fix src package extracts to current dir - Fix segfault in loadpatch process if plugin GUIs are open - Fix segfault when closing patch with plugins in BAD status 0.6.3 --- 2013 . 04 . 23 - New 'solo' button - Portable project system - New 'Single Endless' channel mode - GUI enhancements for channels in WAIT or ENDING status - Minor fixes & cleanups 0.6.2 --- 2013 . 04 . 05 - New volume envelope widget - Zoom with mouse wheel in the action editor - Graphical enhancements & speedups for the action editor - Loop-repeat doesn't stop when put in ending mode (fixed) - Fix draw errors when zooming too much the action editor - Set silence in wave editor messes up the waveform (fixed) - Wrong slashes in file path when saving a patch in Windows (fixed) - Many, many code improvements and bugs fixed 0.6.1 --- 2013 . 03 . 21 - Unlimited number of channels - Deep internal refactoring, mixer/GUI layers - Fix random crashes on exit - Fix crashes when closing Giada with VST windows opened - Always free Master In plugin stack on exit - Lots of other minor bugs fixed and small enhancements 0.6.0 --- 2013 . 03 . 02 - New, full-screen, redesigned sample editor - Zoom with mouse wheel in sample editor - Use kernelAudio::defaultIn/defaultOut for DEFAULT_SOUNDDEV_OUT - Volume knob in main window now updates the editor - Sound system issues in OS X (fixed) - Output device info dialog refers to wrong device (fixed) 0.5.8 --- 2013 . 02 . 07 - Internal samplerate conversion (with libsamplerate) - Bring channels automatically to full volume on sample load - Ability to set the audio device frequency - New "internal mute" feature - fix for deprecated VST opcode 14 - fix deb package issues on Ubuntu 12.10 / KXStudio 0.5.7 --- 2013 . 01 . 21 - visual grid + snapping in the action editor - implement more audioMasterCanDo's in pluginHost - limit zoom in actionEditor - revise zoom behavior in actionEditor, now more comfortable - fix forward declaration & inclusion of several headers - implemented VST opcode 32 - implemented VST opcode 33 - implemented VST opcode 34 - update website link in tar files - update copyright info for 2013 0.5.6 --- 2013 . 01 . 03 - New overdub mode for live recording - Support for VST programs, aka presets - Lots of VST opcodes implemented - Fix crash when removing a plugin from the stack - Fix pops when going to beat 0 - Fix compilation issues without --enable-vst - Many invisible optimizations and small bugs fixed 0.5.5 --- 2012 . 12 . 15 - "Hear what you're playing" feature - Fx processing on the input side - Ability to add different action types (Action Editor) - Desktop integration on Linux (via deb package) - Upgrade to FLTK 1.3.2 - Remove "the action might stop the channel" when loading new samples - Fix wrong positioning of zoom tools (Action Editor) - Fix unwanted interactions on the grey area (Action Editor) - Fix wrong memory alloc during the VST processing - VST don't show up in OS X (fixed) - Minor internal refactoring + bugfixing 0.5.4 --- 2012 . 11 . 24 - VST GUI support - Better subwindow management - Implemented many other VST opcodes - Missing plugins are now shown in the list with a 'dead' state - Refresh action editor when changing beats (via beat operator or beat window) - Graphical improvements in the action editor - Resizable action editor doesn't work well (fixed) - Fix auto fadeout for SINGLE_PRESS channels - Fix compilation without --enable-vst - Fix for a wrong prototype definition of the VST hostCallback 0.5.3 --- 2012 . 10 . 26 - Live beat manipulators (x2)(/2) - New sub-windows management, faster and more comfortable - New optional hard limiter on the output side - Action Editor window recalls x,y,w,h zoom and position - Usability improvements while handling an action (action editor) - Refresh actionEditor window when switching channel mode or delete actions - Unable to delete a killchan action (action editor) (fixed) - Don't show ACTION_KILLCHAN in a singlepress channel (action editor) - Libsndfile no longer statically linked in Linux - Fixed a typo in config: "when the sequeCer is halted" - redefinition of DEFAULT_PITCH in wingdi.h (windows) (fixed) - Upgrade to FLTK 1.3.0 - Other internal optimizations - Other small bugs fixed 0.5.2 --- 2012 . 10 . 05 - Add ability to handle actions for loop-mode channels - Add ability to record live mute actions for loop-mode channels - Lots of live action recording improvements - Enhanced usability for the action editor - More verbose output if kernel audio fails to start - Several internal optimizations 0.5.1 --- 2012 . 09 . 13 - First implementation of the Action Editor - Added compatibility with Ubuntu >= 10.04 0.5.0 --- 2012 . 07 . 23 - New custom project folder (.gprj) - Sample names are now made unique - Fixed unwanted time stretching while exporting a mono sample - Lots of minor internal improvements 0.4.12 --- 2012 . 07 . 01 - VST parameters and stacks are now stored in patch file - Upgrade to RtAudio 0.4.11 - PulseAudio support in Linux (thanks to RtAudio 0.4.11) - Revised .deb package - Enhanced "normalize" function in wave editor - Several memory issues fixed - Internal enhancements and minor bugs fixed 0.4.11 --- 2012 . 06 . 10 - VST stack for each channel - Custom paths for plugins, samples and patches - Crash in config panel if device is busy (fixed) - Graphical bug in the input meter (fixed) - ParamLabel added in the VST parameter list 0.4.10 --- 2012 . 05 . 30 - Ability to shift up an down VST plugins - Enhanced patch/conf architecture - Ability to edit a sample while playing - Mutex controls in VST processing - Lots of security issues fixed while changing pitch dynamically - Enhanced sub-window system - Several minor bugs fixed 0.4.9 --- 2012 . 05 . 12 - No more mandatory inputs - Pitch value properly stored inside the patch - Several small VST host improvements - Enhanced window management - Ability to browse files while playing with main GUI (non-modal browser) - Improved error checking in KernelAudio - Wrong style for lower scrollbar in Browser (fixed) - Fixed compilation on 64 bit systems (thanks to Speps@Archlinux) - Samplerate no longer hardcoded, auto-detected with JACK - Minor internal improvements and bugfixing 0.4.8 --- 2012 . 04 . 21 - Initial VST support (experimental) - Pitch controller (experimental, no filtering) - OSX bundles are now correctly handled by the file browser - Fixed several memory leaks - Minor internal improvements 0.4.7 --- 2012 . 03 . 31 - Cut, trim & silence operations in sample editor - New "Reload sample" button added - Lots of optimizations in the waveform drawing routines - The sample is no longer editable while in play mode - Fixed potential startup crashes while using Giada with Jack Audio - Other minor fixes applied to the configuration panel - Fixed compilation on 64 bit systems (thanks to Speps@Archlinux) 0.4.6 --- 2012 . 03 . 11 - New device information panel - The device configuration now shows only active and available devices - Channel panel no longer pops up during a recording process - GUI beautifications and other minor graphical fixes - Program icon added in all subwindows - Action records no longer available during a take, and vice versa - Fixed a serious bug that swapped input and output devices - Fixed loop behavior in ending mode - Fixed clicks when stopping a muted channel in loop 0.4.5 --- 2012 . 02 . 25 - Complete GUI redesign - New "start/stop action recs" button - Lots of internal cleanups and micro refactorings - Small drawing glithes in Editor and status box (fixed) - An invalid patch puts Giada to init state (fixed) - Fixed button repeat on start/stop, action rec, input rec - Checks against takes with unique name - Message "this action may stop the channel" always shown (fixed) - Channel no longer freeable while a take is in progress 0.4.4 --- 2012 . 02 . 04 - New input/output channel selector - Rewind bypasses the quantizer if triggered via mouse (fixed) - Fixed library paths in configure and makefile (thanks to Yann C.) - Added AUTHORS and NEWS files to the source package (thanks to Yann C.) - More robust sample export procedure - Issues with mute buttons when opening a patch (fixed) - Several usability improvements - Minor code cleanups and optimizations 0.4.3 --- 2012 . 01 . 21 - New "save project" feature - Ability to export a single sample to disk - More feedback when removing/clearing actions and samples - Sequencer starts automatically when action-rec button is pressed - Alert if patch name is empty while saving it - Channels now store internally the name of the samples - Missing "--no devices found--" in input devices menu (fixed) - Alert added if there are no empty channels for recording - "Edit->Clear all actions" no longer works (fixed) - END button could be used as a channel trigger (fixed) - Recorders are available even if device status is wrong (fixed) - Missing sample rewind if channel is muted (fixed) - Quantizer doesn't work if framesize is odd (fixed) - Random segfault when closing Giada (fixed) - Lots of code cleanups - Other minor improvements and optimizations 0.4.2 --- 2012 . 01 . 09 - Live sampling from external input with meter and delay compensation - Check against uneven values and overflow in buffersize field - Wrong normalized values if volume level is 0.0 (fixed) - Boost dial goes crazy if normalized > 20.0 dB (fixed) - Boost dial goes crazy if normalized < 0.0 dB (fixed) - Unwanted noise click if a muted channel is being rewinded (fixed) - Mute doesn't work well for single-shot samples (fixed) - Wrong FLTK headers (fixed, thanks to Yann C.) - Moving chanStart/chanEnd swaps stereo image (fixed) - Reset to init state doesn't reset mute buttons (fixed) - Wrong chanStart value if > 0 (fixed) 0.4.1 --- 2011 . 12 . 07 - Complete mixer engine refactoring - Faster audio buffer allocation - Global beat system revisited - Autocrossfade between samples is now enabled by default - No more recorded actions on odd frames - Unintentional channel swapping fixed - Unable to list all sound systems and sound devs under OSX (fixed) - Missing graceful stop of audio streaming under OSX (fixed) 0.4.0 --- 2011 . 11 . 16 - Support for all major uncompressed file formats (with libsndfile) - Enhanced mono > stereo conversion - Fixed drawing issues for the start/stop labels inside the waveform - Enhanced backward compatibility with old patches - Support for compilation on OS X and Windows 0.3.6 --- 2011 . 11 . 02 - Initial Mac OS X release - (Windows) Ability to list and browse all active drives - Change some internal routines plus minor optimizations - Added -pedantic and -Werror flag to the compiler - Crash if clicking on mute in an empty channel (fixed) - Chan status changes if an empty channel is being muted (fixed) 0.3.5 --- 2011 . 10 . 22 - Pan controller added - New GNU-style source code packaging - Revamped .deb package - Program icon missing under Windows (fixed) - Crash if a sample in patch is missing from the filesystem (fixed) - Unable to rewind to beat 1 if quantizer is on and seq stopped (fixed) - Several minor glitches fixed 0.3.4 --- 2011 . 10 . 10 - Full source code released under GPL license - Autosmooth is now toggleable via setup - Faster loading process of patch files - Various internal cleanups and optimizations - Fixed incorrect reading of boost values from patch - Fixed a potential bug that prevented the config panel to appear - Fixed stereo swap bug - Minor graphical revisions 0.3.3 --- 2011 . 09 . 28 - New "normalize" function - More editing tools added inside the sample editor - Waveform beautifications - Fixed interaction bugs for boost and volume controls 0.3.2 --- 2011 . 09 . 19 - New "mute" button inside the main window - Waveform is now updated when the boost value changes - Zoomin/zoomout relative to the scrollbar position - Fixed garbage output if the volume was "-inf" (windows version) - Fixed several rendering issues for short waveforms 0.3.1 --- 2011 . 09 . 12 - Boost volume + fine volume control in sample editor - Start/End handles inside the editor are now draggable via mouse - Fixed scrollbar issues in sample editor - Start/end points are now always drawn in the foreground - Waveform no longer overflow if a value is greater than the window - (linux) giada.conf is saved inside the hidden folder /home/.giada - patch loading process is now faster and cleaner - Update to rtAudio 4.0.10 0.3.0 --- 2011 . 09 . 01 - New sample editor window - Ability to set start/end points within a sample - Update to rtAudio 4.0.9 - Fixed an string overflow inside a patch - Fixed a missing memory free if a sample is unreadable - Several internal updates and optimizations 0.2.7 --- 2011 . 07. 22 - New way to handle recorded channels as loops - Fixed retrig for backspace key (rewind) - Enhanced rewind with quantization support - Main and alert windows now appear centered on screen - Sanity check against old patches without metronome information - Rewind now affects loops in rec-reading mode 0.2.6 --- 2011 . 07 . 11 - Internal metronome - Fixed some glitches in config panel - Minor cleanups 0.2.5 --- 2011 . 06 . 20 - Configuration panel redesign - Several new control options - Progress feedback when loading patches - Internal optimizations - Updated docs 0.2.4 --- 2011 . 06 . 08 - New loop repeat mode - Ability to save patches anywhere in the filesystem - Sub-beat management - Sound meter has been revisited and improved - Several patch enhancements - Core audio optimizations 0.2.3 --- 2011 . 05 . 18 - ASIO support for Windows version - Enhanced security when reading values from a patch - Ability to disable the recordings when the sequencer is paused - Master volume and rec status are now saved inside the patch - Device selection fixed and improved - Sequencer flickering in Windows has been fixed - Feedback added if a sample from a patch is unreadable or corrupted - Minor internal optimizations 0.2.2 --- 2011 . 05 . 04 - New open-source patch system - A patch can now be loaded from any location of the filesystem - Enhanced file browser coords system - Lots of minor improvements to the sample loading/unloading procedure - (win) Init path of file browser now starts from %userProfile%/Desktop - Wrong handling of "/" chars fixed in config menu - Fixed potential hangs on quit - Fixed clicks when stopping sequencer/sample - Minor gui beautifications 0.2.1 --- 2011 . 04 . 26 - Windows version 0.2.0 --- 2011 . 04 . 19 - Full JACK and ALSA support with RtAudio - New list of sound devices in menu window - Enhanced shutdown procedure to prevent potential crashes - Some GUI glitches fixed - Fixed random locks when the screensaver is active 0.1.8 --- 2011 . 04 . 13 - new functions: free al samples/recordings, reset to init patch - main menu redesign - the file browser is now resizable - GUI feedback for samples in play mode - some fixes when unloading a sample 0.1.7 --- 2011 . 04 . 07 - Ability to remove only action recordings or mute recordings - Shift+key now stops the sample if the master play is deactivated - Frame 0 was always processed at the end of the sequencer - Minor internal improvements 0.1.6 --- 2011 . 03 . 29 - Autocrossfade to prevent clicks - Internal improvements and bugfixing 0.1.5 --- 2011 . 03 . 10 - decimal bpm adjustment - ability to shrink/expand actions when changing the global beats - improved GUI for beats and bpm controllers - improved routines for action management - actions are now updated when you change bpm 0.1.4 --- 2011 . 03 . 04 - ability to save recorded actions - status box now shows if a recorded chan is deactivated - recorder is reset correctly when you load a new patch - minor improvements 0.1.3 --- 2011 . 02 . 26 - action recorder (first implementation) - quantization procedure slightly optimized - minor graphical adjustments - expanded documentation 0.1.2 --- 2011 . 02 . 08 - master volume controller - improved sound meter with more accuracy - improved verifications when reading or writing a patch - beat counter is now always reset to 1 after a patch is loaded - made loading wave files more robust, plus memory optimizations - minor crashes fixed 0.1.1 --- 2011 . 01 . 26 - expansion to 32 channels - GUI restyling - live quantizer - fixed wrong handling of "mute" value when loading a patch - minor internal improvements 0.1.0 --- 2011 . 01 . 18 - ability to mute channels - stop and rewind buttons now affect only channels in loop mode - undo for ending loops - internal patch improvements to provide backward compatibility - better behaviour when exceeding the total amount of available memory - fixed random reversals of stereo field at the end of the beat bar - fixed a potential segmentation fault when freeing a sample 0.0.12 --- 2011 . 01 . 11 - ability to free a channel - "stop" button to suspend the general program - new "stop-to-end" mode for looped channels - new "full stop" key combination - enhanced mouse interaction - minor bugfixing 0.0.11 --- 2010 . 12 . 28 - customizable keys - GUI layer optimizations and improvements - overwrite confirmation when saving a patch - the browser always displays the patch folder when loading a new patch - browser url is now read-only to prevent manipulations 0.0.10 --- 2010 . 12 . 16 - new "single-mode retrig" mode added - expansion to 16 channels - new advanced file browser with the ability to navigate the filesystem - audio configuration now uses the "default" device, if not changed - graphical restyling for audio channels - fixed a random crash on startup, due to a wrong thread synch 0.0.9 --- 2010 . 12 . 08 - new loop once mode - new graphical beat meter - rewind-program button added - heavy buttons and controls restyling - reinforced header verification when a new patch is opened for reading - some bugfixing for the loading procedure of a patch - fixed a potential crash while a new sample is being loaded 0.0.8 --- 2010 . 11 . 28 - fixed a critical crash while loading a sample - GUI warning when loading a sample or a patch into an active channel - little optimization during the search for data into waves - all popup windows are now modal (always on top) - fixed a potential crash in case of malformed wave files 0.0.7 --- 2010 . 11 . 18 - new peak meter with clip warning and system status report - any "ok" button is associated to the "return" key (for fast inputs) - graphical improvements for checkboxes, buttons, smaller fonts in browsers - graphical feedback for missing samples - internal optimizations 0.0.6 --- 2010 . 11 . 01 - new 32 bit floating point audio engine - support for any wave bit-rate, from 8 bit pcm to 32 float - Giada now prompts when a sound card error occurs - removed the hard-limiting system, now useless - the "save patch" panel now shows the actual patchname in use - alphabetic sort into the file browser - fixed an annoying gui flickering - patch volume information are now handled correctly - minor internal optimizations - fixed a memory leak when loading a new patch - other memory optimizations 0.0.5 --- 2010 . 10 . 21 - Patch-based system: load/save your setup from/to a binary file - New audio configuration panel - New configuration file (giada.conf) where to store data - Complete implementation of the double click startup - Fixed a bug related to the confirm-on-quit window - Minor GUI beautifications - Extended documentation 0.0.4 --- 2010 . 10 . 11 - New internal sample-accurate loop engine - Ability to configure the period size through ini file - First implementation of the double click startup - Debug information are now properly tagged, reporting the interested layer 0.0.3 --- 2010 . 10 . 02 - (giada) New official logo - (giada) Ability to load single-channel samples - (giada) Capital letter consistency between GUI buttons - (giada) Added "cancel" button to the browser window - (giada) Endianness verification - (giada) Cleanup of the audio initialization procedure - (giada) Several internal optimization for audio playback - (giada) ALSA layer now tells if an underrun occurs - (giada) Internal memory allocation improvements - (giada) Fixed an unallocated hardware parameter into ALSA configuration - (wa) Information about wave endianness - Added a "Requirements" section to the readme file 0.0.2 --- 2010 . 09 . 17 - (giada) More visual feedbacks if a key is pressed - (giada) Added a graphical alert if a sample is in an incorrect format - (giada) Confirm on exit - (giada) Graphical improvements for the browser window - (giada) Browser window doesn't close itself anymore if a sample format is incorrect - (giada) Added "-- no sample --" for empty channels - (giada) Startup no longer fails if a sample from the ini file is not found - (giada) Internal optimization for the sample loading routine - (giada) More graphical consistency between subwindows - (giada) The sample name is now truncated to fit into its box, preventing overflow - (giada) Other minor GUI tweaks - (giada) Internal memory improvements to prevent a bad bug of allocation with malformed wave files - (wa) More information about sample size - (wa) Added calculations and comparison between data sizes 0.0.1 --- 2010 . 09 . 06 (initial release) giada-0.22.0/README.md000066400000000000000000000103541425106661500141300ustar00rootroot00000000000000

Giada - Your Hardcore Loop Machine

Giada - Your Hardcore Loop Machine | Official website: giadamusic.com | Build status

## What is Giada? Giada is an open source, minimalistic and hardcore music production tool. Designed for DJs, live performers and electronic musicians.

✦✦✦ See Giada in action! ✦✦✦

![Giada Loop Machine screenshot](https://giadamusic.com/images/giada-canvas.png) ## Main features * Your sample player! Load samples from your crates and play them with a computer keyboard or a MIDI controller; * Your loop machine! Build your performance in real time by layering audio tracks or MIDI events, driven by the main sequencer; * Your song editor! Write songs from scratch or edit existing live recordings with the powerful Action Editor, for a fine-tuned control; * Your live recorder! Record sounds from the real world and MIDI events coming from external devices or other apps; * Your FX processor! Process samples or audio/MIDI input signals with VST instruments from your plug-ins collection; * Your MIDI controller! Control other software or synchronize physical MIDI devices by using Giada as a MIDI master sequencer. ### And more: * Ultra-lightweight internal design; * multi-thread/multi-core support; * 32-bit floating point audio engine; * 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 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 [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. ## License Giada is available under the terms of the GNU General Public License. Take a look at the COPYING file for further information. ## Documentation 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 [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 in the [discussions area](https://github.com/monocasual/giada/discussions). ## Copyright Giada is Copyright (C) 2010-2022 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. 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 . giada-0.22.0/extras/000077500000000000000000000000001425106661500141545ustar00rootroot00000000000000giada-0.22.0/extras/com.giadamusic.Giada.desktop000066400000000000000000000012371425106661500214410ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Type=Application Name=Giada GenericName=Drum machine and loop sequencer Comment=Drum machine and loop sequencer GenericName[de]=Drum Maschine und Loop Sequenzer GenericName[es]=Caja de ritmos y secuenciador de loops GenericName[fr]=Boîte à rythme et séquenceur de boucle Comment=Drum machine and loop sequencer Comment[de]=Drum Maschine und Loop Sequenzer Comment[es]=Caja de ritmos y secuenciador de loops Comment[fr]=Boîte à rythme et séquenceur de boucle Exec=giada %f Terminal=false Icon=com.giadamusic.Giada Categories=Music;AudioVideo;Audio;Midi;X-Digital_Processing;X-Jack;X-MIDI; Keywords=midi;jackd;alsa;pulse;audio;sound;loop; giada-0.22.0/extras/com.giadamusic.Giada.metainfo.xml000066400000000000000000000043261425106661500223730ustar00rootroot00000000000000 com.giadamusic.Giada CC0 GPL-2.0+ Giada Your hardcore loop machine

Giada is an open source, minimalistic and hardcore music production tool. Designed for DJs, live performers and electronic musicians.

com.giadamusic.Giada.desktop https://www.giadamusic.com/images/screenshots/giada-loop-machine-screenshot-03-large-project.png A fairly large project with samples and MIDI events https://www.giadamusic.com/images/screenshots/giada-loop-machine-screenshot-17-midi-action-editor.png New Action Editor for MIDI events https://www.giadamusic.com/images/screenshots/giada-loop-machine-screenshot-01-sample-editor.png Chopping samples in the advanced Sample Editor giadaloopmachine_AT_gmail.com https://www.giadamusic.com/ https://www.giadamusic.com/forum
  • 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)
giada-0.22.0/extras/giada-logo.png000066400000000000000000000110101425106661500166560ustar00rootroot00000000000000PNG  IHDRyW pHYs*ۑtIME )/bKGD̿IDATx]ytU ڥWK*'%/} $̓äXE+) * X j:`EAL!`$1 $sν ?bw﾿>D1F_܍$Q`.獼K+51߼=|_W~/S.&>g{$~"9?&--f^/B2'xC0C/z|kU`DDގ; S)6yUu/#yψt |?uqe6þJg1` V__ӉUnѥ 2z-ܐPP1v `Yq oMHsc6<*l4) ͡[*0j4-rIo8(~&"JsU*0rw v#E}_8 2Rd$# IHF2R 2 $``vY)_߯z%0qVxT&.`;ǐ{ن{? :=zMs9Ej# U d`ohѨ&S=(O0x+Yu|YѐpPf%kYTBFc LPL,͘t*׫5 UӘþjfU5%6|:`+M>DU9kFBXYjVuS{hxq;TyUkL͂Y-AY 5fRl XmCN}R઼BWZ{A%13l|(ApQyY}?VQ&D#/'1 ptÅ4c0OaJ)m+#רȇ*GH1X1;}bPdaAU n—ȇW@*rP= <ـXG&a<- dc"mVԞ)=aW6HNI$ Í?-: hE7u3Ske"`9[ $mHpQ[s2y5lXlTU ,?in9T ӪX;5:6ا_ۃ#.kg,[,s0X]1NȊgpKlF wUq;8!8koSbB&ImVwA [$`]W,3!+Kme&v[Hi#h/ZTiW^,hZ^A16=d98=V p"΍D^#6n +*c| ! _:^[xG~˗rb?!ÑH /uM//'iKP4|%! Ć]Ԝ=(>e UD NG׽2U/DDҍ*LfX ޵5҆0uTA*.ťF$R=pVtcŲ}@5=`z*XE]2 ܥY2htTO!)ܓ"3oT#~#i>CMU8*մ|KmVȲ^7`0Fڊ{:Fwa2Q;XLļ 2TLt`s+^?4d@mû8*Kpc >vq*b֟39t K;:`!Oʪ=/hefEBt(wFP-b1`,equC].ੲ0 6OcW[HR%\nt܄A{Ԏi( DLD[5ޟSDd\΃䝆1צ+(l1~N{ 2虛`OqU=?"S^gx ujH%< +lGhcuknbyu!z*>URScx5V~]|9wpC̽ ]#%"+s| /33ֻGKӼs{yr%Ws#7q#K.O]7u{i}@EXIENDB`giada-0.22.0/extras/giada-logo.svg000066400000000000000000000051661425106661500167100ustar00rootroot00000000000000 image/svg+xml giada-0.22.0/extras/giada-logotype.png000066400000000000000000000204461425106661500175750ustar00rootroot00000000000000PNG  IHDRǕf pHYs*ۑtIME (bKGD̿ IDATx}wTEF|Q++| 3tש!D1bDPL~ Eׄ""I<=+ܾG?[uy:uQ/ْ⣣DGr*͋y5r ¥Oq<|<#U rpp "Yons1,xâm%<U܃upmh+蔼͓C:pOw"mXO>8-ݳvkxW j;x5ߢ;v1R=䧁R^Ȉlힿo1'ü2nKny QW2~[(:lRմ 1i3p8&Vlqo{qh[>[3VgfОdKE#f5j/GޔCߔ+eqfљ1{O65r;Rvh Kچn]Q!)EDr?9QѪ]ʪ1ND|2៸GP#tDG |Or.̴ 4K+Q(G9ʰ +1\e|{»SQ`(x+PHvc)D\A(@(E=&p!cPDDbR*0:ab%JLZ ;1}H*Ã2"T]Wa VtRl nD8r|)7Z q7>6hU`B2\bDSEm(P&y%ޮCjŶp6p+in 8A/ĺP0jU5jvt!"qUP`8Tu 6.+"Ǒ!{,; 3#{!?0XȢ Ǜ_b"ȷ 1R_mf~L8 C'E 碛t%X"6l r)b8{d2_ F܉TJ#kJb4dsplme;#9'fy8WDxn+,̪{Ǒ"-"MdyGHGj("lnmiiZb#5iB= 4LE?8F2o@P7뼘I_|,\b.Ylv=HYIou~C|U=DŽGD|?3O󰎑 L/{b>aR D_nt%'wi..MV&ׂ({[XM- %gbz,sz;ZD(SlMrGQHpz3r]9 pZwRD$-iUӞDDq':h_٪.~'9Rnl9[$12Ƭq66z$bEƔf#;a0έ5iM{9(& /4F>"H~#F,QB)&"_ظ4訆Vlssi[|d! imz'<xXJ:?qSf~S5B>Z DKXhGq~u7z ߣO `'zŅ),5>4.1 e0 N-0F=6>it?w;qHk=Ad͘`ʈOGOfZ 9HZ5E6:}̧S,3<, ףnAk-iV9>,ٟ4e$&Y y1Şm/[h@e!Wf:=8^e Yhlj'h &!?zZ`Tg xQV)\n肭c%րfF#^ηr%/|2v %IPk7?OioxǴ|OO Y]"6^~ ;!xmVKxC03FAj-ه=jj_QL8<ى ˱Ԃ >d(6[Rs%@j Kb&"Kji3"Pe"\ ZI4'nWI=F'jmM|ULHWiU@8Rw(8W7ī,qY0(f؉gY2h+dԕGic;9xVpZoO FqL!QrHt.XF[nޠ F ; X1Ԫ7PdՊX{1AZjxF냵3]xC(֋WjU0%w6tE87U3tUKjM6H"˒X7҆!|g =ֈ6'E/5H^TUDo*v2="%όK5&7ɂZ5#,0 H>MZ |HH?ӣ5o%Վ6BDDD)~ӻPgaIZc,7-Q=ْ=,:(Nq웲+ss:UBb_M4dP}L)W$o'0BJHq͔1ݠ`p$^Q$SkYz(7ZKd>)Gpڷ8; Ur~񬕐,bCJy \fkeǙRyBR+DD|I4TXS:%e+2g„QcjRMvݩV} y )&܍ P#Rjm}J-""e 5Lk9&-ZKV8+>RAQɺ_ XhE.1~ZӚ#I,/+F5Cϓ$ɾZ-Pr]:UԄ'ݞEx$ƪ# Sfp;mv#R6Q]{+j=7?ksZʟR}b-98<.u:uPf)[?! ?:gfEZB|n. 2hjm~SKuҵt=+-ԺMsdTkcU^qx l]+egyW9>Pk5zh+#eaN=Qk5ZG)s:? R'LږZ"O){"KL&`h:3F9u |Y[-Je34Q8Uܔ_Sm\t.=+Z}lO⒣9׋!nKhM?.Z&Il.ɀZjoZK]SePRK騵}d&#nM cEw ¿@ FF!B!a xC |3oA#BInaDK~v/R{Ssz3T3~__3'Z3F8-fdtL0c4RR.HbZqySv/f >V aՊW[Qk}}g\,ZOs%%8r *dVo⻽߹&O4NGƋI{NɃ0֞JR@`T"_lho4im}3 Z@J/9.EESwzN,3B`P%uly`4q5wcB Rw(݃+G4O/ߛIַ_ԊfC,{yO]zuE_dGj%)X (,Nh,ؠ?s-5^#n b"E-JkI44y!Z,7r|{[Ԏi'7-i`QvfzbP?6rc!DtO sM|kt2+TM:;/L $[QkN{[7 Hdt[%EI17bUKi=!Ky!lv$!^kHeRmqgyyxPo6:JŒc:?bpL` E. Vf[j𫚣uMYYJ+y<k|dXB[ϏvX\3HAkET˓pƚ>j>qV5|>lBc5ަ[ZD2ϋV/}YePT@gsZinj3Pix,.zk.[)݁WΉ^? 1ȝ|wĊ%1yhjODԥ&'<"*Za"RI$EI"&V׬3`ޢ%Foш zѾrl@*<tGU?4xR6Ѥǜ)[xc[_ %*愖Z#Uk=?J?ɳoyz[{1P}SLAרqm;k~ٍeS/34i*"X$<Tn?jc~k7h0kP ֏k%:%=Y;D!o5+x=em}Asuhp ?sn6{x]aKg&,>hB-9g%k:y2q~?9_Hl~V#V~_gg,DVB&X9 ʐӹćsn+ۉ.y#_,E;c5 O=\iNd-~l $e&DTEbh.mN[mW5QEYMhED]&gTA?_Է7jWVYXH$_#e=Q/q"7_C9@%Zd'f|eEQkpJvu1^K p5;e:>+ nh E G"荑 .vH[x$ߎpc؎RX1,le5Gt⊐cD8(qp!˚W 뎗'(Ƀ)3 &Asw tp%3Ka$j׀OY)_wR3d8{Mu5(8ߒ{7rjmY;Ap2tQ݈uև^e}: pXD 8CiMsR˂ B'cȦtk޽C8KaFV{+j޽CND dfQ *9-ǩd1u'f<_`RB#V]I0->nMrHdk1p6g8vAVb_ #ؑvA$D[rH+\1ݵ(/*kW1`Ce~}$OL?;84x/!pxrn)Ѐ(lʏz~JXIx znި1B a 6=\1 ?b,¿4H9j/_RClEMX0bED+EwF_}2x1T ""lu0 S=~n 1&,ã莼Ԫv:Cf!v\a <{Q.,k1y.Py8mu !╶%\0a&> lBʱ eX!^£}RR`G>ҽECw" dG.:!9舎A:!(@_b,-2֤m%T?=v]AY} 弭QjCcIDi hS#ǹIwݜ9>Mmٯ{Wlŝ&?7О"-/DZd~a3H1<.twCFL|ߕoj$sᷴ:_k`bU,&~sf q lZ}ͷq["!D,8{OӺ|ò{ ? s堿:cC[D_ۼԼ gdy<`j'ҔA(>AJ)<*.27J|ܖDC)x )IENDB`giada-0.22.0/extras/giada.icns000066400000000000000000002505331425106661500161070ustar00rootroot00000000000000icnsQ[ic09QS jP ftypjp2 jp2 Ojp2hihdrcolr"cdefPjp2cOQ2R \@@HHPHHPHHPHHPHHPd%Created by OpenJPEG version 2.1.2 PR~,@I'k:M+=z?ҭn;G_Ve9 z~A0Yף'΀~cf?]lq* p%D6NeO.,Xs1Wk6fŋz AG%X2_@GdRwht.%Wc4% KA2@]aH|U@z *"A#&> Y U甡P# >P:/C~,@I'k:M+=z?ҭn;G_Ve9 z~A0Yף'΀~cf?]lq* p%D6NeO.,Xs1Wk6fŋz AG%X2_@GdRwht.%Wc4% KA2@]aH|U@z *"A#&> Y U甡P# >P:/C~,@I'k:M+=z?ҭn;G_Ve9 z~A0Yף'΀~cf?]lq* p%D6NeO.,Xs1Wk6fŋz AG%X2_@GdRwht.%Wc4% KA2@]aH|U@z *"A#&> Y U甡P# >P:/C}nQ]{|Kw<_ I(9r.dd Ujsy'|^L X{pXh_ޗ(ߐC8ۏgf{sqzg{Lj0/m>΂a(1 ǵimr]X>]W|A'189Ď {-9uϾΟ}?+|pߨaO7TMD /+MqͳT%YԦF"KI,=YFsm`I"ԋB-hbi?>5Y KnPzsA=oZ7j1 ZO)Xq]d0ջ*_p!Ϳm$ V{M~G\ycmpZdwMRFf%c"p>GӚM->ϾΟ}?+|pߨaO7TMD /+MqͳT%YԦF"KI,=YFsm`I"ԋB-hbi?>5Y KnPzsA=oZ7j1 ZO)Xq]d0ջ*_p!Ϳm$ V{M~G\ycmpZdwMRFf%c"p>GӚM->ϾΟ}?+|pߨaO7TMD /+MqͳT%YԦF"KI,=YFsm`I"ԋB-hbi?>5Y KnPzsA=oZ7j1 ZO)Xq]d0ջ*_p!Ϳm$ V{M~G\ycmpZdwMRFf%c"p>GӚM->ߓ=Ԋ81Fg} ⃃wYV\ am731ʄ|P1vQc>t\=7oYԀ ڿ*CSp1Pѭ `opw=niU7W 30-R7pݕ^/6 xPղ׳)+D_i%Mc!Gz 9.xODv!|̎5E>ol$w;A/VwMϦ'sFmaļj#XG4\ER5+jN ]=bE['5j*{UiŅxϿkG4UL7OO{|i1-QGS%+~tr*7-$iLIuJzH{#6[3\P`[vVxzv v&C ߙ":(پ)謬=aݽ+,/+ A%~\)=q_DQ;640KwвLq&fpcP!|ѸQBd<x[9$b.;54?%S]-Qiٯ +rcnI'`d`zCqD"J!W*iI]dT(-TyVBBe13'w9.Dn2NzP5]@+HtjICCp|e et^Ux+;㉰jd"0{qB E\)\hw %V\wB3+Ke͚쾱 gFfz#jx=c@4i>*l%&A[bw|cX,V̟S5'"q .x'w,Fƒ=bV!Tҭ.6Z[-~|lK6?5T\W~>1 n:|>PL"禋oBR8Gq}VX#JK~i2C!~ցuJl*s~TZD/-VbQV0ӟKI!fd"1v$UN[=.ueW Wi/IRc1&I\;U73xümj|Gdrrj's1rߚdֈD(L᪃YݏvFe ߉]RUC%.P{iN&Si&0I|BzZjH<EA^y[OUUBYwo;&u"Kasߏ|ۥk=y'7Rpy"D=_4ل>c$tɕU`C&5? l5^@iI;*!(gxM_JPAto*[M_2 9TI,(ԈCpk:cA5c@###XS7wU(Dwn4'K$>d. (K'ϿkG4UL7OO{|i1-QGS%+~tr*7-$iLIuJzH{#6[3\P`[vVxzv v&C ߙ":(پ)謬=aݽ+,/+ A%~\)=q_DQ;640KwвLq&fpcP!|ѸQBd<x[9$b.;54?%S]-Qiٯ +rcnI'`d`zCqD"J!W*iI]dT(-TyVBBe13'w9.Dn2NzP5]@+HtjICCp|e et^Ux+;㉰jd"0{qB E\)\hw %V\wB3+Ke͚쾱 gFfz#jx=c@4i>*l%&A[bw|cX,V̟S5'"q .x'w,Fƒ=bV!Tҭ.6Z[-~|lK6?5T\W~>1 n:|>PL"禋oBR8Gq}VX#JK~i2C!~ցuJl*s~TZD/-VbQV0ӟKI!fd"1v$UN[=.ueW Wi/IRc1&I\;U73xümj|Gdrrj's1rߚdֈD(L᪃YݏvFe ߉]RUC%.P{iN&Si&0I|BzZjH<EA^y[OUUBYwo;&u"Kasߏ|ۥk=y'7Rpy"D=_4ل>c$tɕU`C&5? l5^@iI;*!(gxM_JPAto*[M_2 9TI,(ԈCpk:cA5c@###XS7wU(Dwn4'K$>d. (K'ϿkG4UL7OO{|i1-QGS%+~tr*7-$iLIuJzH{#6[3\P`[vVxzv v&C ߙ":(پ)謬=aݽ+,/+ A%~\)=q_DQ;640KwвLq&fpcP!|ѸQBd<x[9$b.;54?%S]-Qiٯ +rcnI'`d`zCqD"J!W*iI]dT(-TyVBBe13'w9.Dn2NzP5]@+HtjICCp|e et^Ux+;㉰jd"0{qB E\)\hw %V\wB3+Ke͚쾱 gFfz#jx=c@4i>*l%&A[bw|cX,V̟S5'"q .x'w,Fƒ=bV!Tҭ.6Z[-~|lK6?5T\W~>1 n:|>PL"禋oBR8Gq}VX#JK~i2C!~ցuJl*s~TZD/-VbQV0ӟKI!fd"1v$UN[=.ueW Wi/IRc1&I\;U73xümj|Gdrrj's1rߚdֈD(L᪃YݏvFe ߉]RUC%.P{iN&Si&0I|BzZjH<EA^y[OUUBYwo;&u"Kasߏ|ۥk=y'7Rpy"D=_4ل>c$tɕU`C&5? l5^@iI;*!(gxM_JPAto*[M_2 9TI,(ԈCpk:cA5c@###XS7wU(Dwn4'K$>d. (K'ߝ~W4yA*i O=K~6riB˨Ac C1r;T@6DҡeI/U*!=$* q"LA޵3N~; ny3T韌LpAQ <Ɨ*G=9+AQ|du7-E_o1c^Q֎B@g$mەx!j_`1U/%+Dy,DYm/G߶t4 5WÓ!#dF@Ng#,W]D,mٺ|84ZxqMeg!Vl >5n!8ǰ,,L%V#OT>0lql}1! >Ÿ҈JUO00E[vtgi<f+/\N`q=cUHu(t2T2ؚ9.gƴ";1̴lBba=PgZʺ-#XSlF;%)bzs6O!>v!JCp+QBe^ }O K.1;0DX@=O jCzc""U~:bi٤ _oK;$ͿVbw5O5 ~W@Ttt{M}|&=/c#ʞnٹKCȲy=J5dj!62c5{*R_C\޺EM9)?P:$sKw(6jF}|%d/!mphϮm\f\I]3i+") ^ȑwSeyRH-97{go~bhq).LBoMuZzhPk7QJ,r6B:j*RoM;:Mm^eغzJ_ՀfkHԢG"IW>}s, }0X(HDzY=f*+T4}ouZpDnE/An SUVl%2 < q , K'6AT~MJf-VdXsdUT}>,!x*͊Y^,9w+n"(h+FM䎗{tS~@$˦;gBS` Xb&2&GOѨ^w̺\_C 0Eן H`el0 o]Ng}uGyEBo6;1`8uH,`u.IO\rb-JJD74}sQq >U7d,P5G*,2]%B9ߒuRZm(3$]LUg7=P701W6#i?fF{@L CbğyhD6CV# .kxIRlЍj/#"f-Tiv8R(Һc!MΔ p锇J'G'}kJLL=O 2,WI)`L})-z;9-[ݏ n<4F !}]f$I} [4sr&3o"sʶn1+gCa/ܖ4l;u0܉.*s%2uLJ<z>XЎ5Ley^H˓ۆV(u2u`G( \Q~`'9`4:~vQ$;n#R~S7^EA:7_B!CH rC9QIçKd3jȕeDJ\ /g7?|*zT6q/|HDZr^y{$yH(R%2>,lU=sS,.j7n:!w^i@W:_|6Tpt93!Tل%\\b֧P&\ >DThOj.qҊT1 =ы*EY({L. O{=K ˰ ˟*z.% wstά{) f鶖J\*wᑂ89y3͎ @V-~y D9$lؐRIwqm.gK &SMrk_8_.rR$ޓ4pOEҕ%9ƍٙ933Ya6yֻ7_I: a;)F͋JR, irS;^$d:\hcmAblas5dYl8I&疴k%0!Y1ۣ  ?KPG}E Kݣx6w8q<ɵs 5DO'"_XP2݇mbn%^knY3!^["m0*AwNaU+&qV݄ N3$yO Wo 5Cي;C pK-s)Ѿ3CThҤ(5F;i1l7Ld_J70m D^:o3 &X6Xc^cZ'6f燼0X6mcπxn:ET:P?T3>LJs%9C)sgyک=4s^.fv] :/UPbضgG8Lo-v xQ ΏɧUR=3l*?PP}\ì9q .$c )X̣6/Rꦍ^kX(~pgc9g9] \ rN$fg}i՘35|rwNUYsw"~Inn7%VdUjb IQf=MIa,k, a;M^!42^;[lRS0]c?2v$?6gEl-%㜚#3Ȃq *75$ 2"\;/jyayԀMзONH3, Pf_i 6(Iq[0={۩PS/"abx_J-hE/|XG:+gs3{y~{!Gh3![@ {9 r.=JW}vXֵRο-$(䯳yvTmIv~&TK;h+w0 h%\߹1Vjє{ADҀ$es?mlK:yR.wx34l4E 5 /V (FL8$,&g |73@0!up0Řᛂ&=Te8ax pxf@7T{hY_, >Ÿ҈JUO00E[vtgi<f+/\N`q=cUHu(t2T2ؚ9.gƴ";1̴lBba=PgZʺ-#XSlF;%)bzs6O!>v!JCp+QBe^ }O K.1;0DX@=O jCzc""U~:bi٤ _oK;$ͿVbw5O5 ~W@Ttt{M}|&=/c#ʞnٹKCȲy=J5dj!62c5{*R_C\޺EM9)?P:$sKw(6jF}|%d/!mphϮm\f\I]3i+") ^ȑwSeyRH-97{go~bhq).LBoMuZzhPk7QJ,r6B:j*RoM;:Mm^eغzJ_ՀfkHԢG"IW>}s, }0X(HDzY=f*+T4}ouZpDnE/An SUVl%2 < q , K'6AT~MJf-VdXsdUT}>,!x*͊Y^,9w+n"(h+FM䎗{tS~@$˦;gBS` Xb&2&GOѨ^w̺\_C 0Eן H`el0 o]Ng}uGyEBo6;1`8uH,`u.IO\rb-JJD74}sQq >U7d,P5G*,2]%B9ߒuRZm(3$]LUg7=P701W6#i?fF{@L CbğyhD6CV# .kxIRlЍj/#"f-Tiv8R(Һc!MΔ p锇J'G'}kJLL=O 2,WI)`L})-z;9-[ݏ n<4F !}]f$I} [4sr&3o"sʶn1+gCa/ܖ4l;u0܉.*s%2uLJ<z>XЎ5Ley^H˓ۆV(u2u`G( \Q~`'9`4:~vQ$;n#R~S7^EA:7_B!CH rC9QIçKd3jȕeDJ\ /g7?|*zT6q/|HDZr^y{$yH(R%2>,lU=sS,.j7n:!w^i@W:_|6Tpt93!Tل%\\b֧P&\ >DThOj.qҊT1 =ы*EY({L. O{=K ˰ ˟*z.% wstά{) f鶖J\*wᑂ89y3͎ @V-~y D9$lؐRIwqm.gK &SMrk_8_.rR$ޓ4pOEҕ%9ƍٙ933Ya6yֻ7_I: a;)F͋JR, irS;^$d:\hcmAblas5dYl8I&疴k%0!Y1ۣ  ?KPG}E Kݣx6w8q<ɵs 5DO'"_XP2݇mbn%^knY3!^["m0*AwNaU+&qV݄ N3$yO Wo 5Cي;C pK-s)Ѿ3CThҤ(5F;i1l7Ld_J70m D^:o3 &X6Xc^cZ'6f燼0X6mcπxn:ET:P?T3>LJs%9C)sgyک=4s^.fv] :/UPbضgG8Lo-v xQ ΏɧUR=3l*?PP}\ì9q .$c )X̣6/Rꦍ^kX(~pgc9g9] \ rN$fg}i՘35|rwNUYsw"~Inn7%VdUjb IQf=MIa,k, a;M^!42^;[lRS0]c?2v$?6gEl-%㜚#3Ȃq *75$ 2"\;/jyayԀMзONH3, Pf_i 6(Iq[0={۩PS/"abx_J-hE/|XG:+gs3{y~{!Gh3![@ {9 r.=JW}vXֵRο-$(䯳yvTmIv~&TK;h+w0 h%\߹1Vjє{ADҀ$es?mlK:yR.wx34l4E 5 /V (FL8$,&g |73@0!up0Řᛂ&=Te8ax pxf@7T{hY_, >Ÿ҈JUO00E[vtgi<f+/\N`q=cUHu(t2T2ؚ9.gƴ";1̴lBba=PgZʺ-#XSlF;%)bzs6O!>v!JCp+QBe^ }O K.1;0DX@=O jCzc""U~:bi٤ _oK;$ͿVbw5O5 ~W@Ttt{M}|&=/c#ʞnٹKCȲy=J5dj!62c5{*R_C\޺EM9)?P:$sKw(6jF}|%d/!mphϮm\f\I]3i+") ^ȑwSeyRH-97{go~bhq).LBoMuZzhPk7QJ,r6B:j*RoM;:Mm^eغzJ_ՀfkHԢG"IW>}s, }0X(HDzY=f*+T4}ouZpDnE/An SUVl%2 < q , K'6AT~MJf-VdXsdUT}>,!x*͊Y^,9w+n"(h+FM䎗{tS~@$˦;gBS` Xb&2&GOѨ^w̺\_C 0Eן H`el0 o]Ng}uGyEBo6;1`8uH,`u.IO\rb-JJD74}sQq >U7d,P5G*,2]%B9ߒuRZm(3$]LUg7=P701W6#i?fF{@L CbğyhD6CV# .kxIRlЍj/#"f-Tiv8R(Һc!MΔ p锇J'G'}kJLL=O 2,WI)`L})-z;9-[ݏ n<4F !}]f$I} [4sr&3o"sʶn1+gCa/ܖ4l;u0܉.*s%2uLJ<z>XЎ5Ley^H˓ۆV(u2u`G( \Q~`'9`4:~vQ$;n#R~S7^EA:7_B!CH rC9QIçKd3jȕeDJ\ /g7?|*zT6q/|HDZr^y{$yH(R%2>,lU=sS,.j7n:!w^i@W:_|6Tpt93!Tل%\\b֧P&\ >DThOj.qҊT1 =ы*EY({L. O{=K ˰ ˟*z.% wstά{) f鶖J\*wᑂ89y3͎ @V-~y D9$lؐRIwqm.gK &SMrk_8_.rR$ޓ4pOEҕ%9ƍٙ933Ya6yֻ7_I: a;)F͋JR, irS;^$d:\hcmAblas5dYl8I&疴k%0!Y1ۣ  ?KPG}E Kݣx6w8q<ɵs 5DO'"_XP2݇mbn%^knY3!^["m0*AwNaU+&qV݄ N3$yO Wo 5Cي;C pK-s)Ѿ3CThҤ(5F;i1l7Ld_J70m D^:o3 &X6Xc^cZ'6f燼0X6mcπxn:ET:P?T3>LJs%9C)sgyک=4s^.fv] :/UPbضgG8Lo-v xQ ΏɧUR=3l*?PP}\ì9q .$c )X̣6/Rꦍ^kX(~pgc9g9] \ rN$fg}i՘35|rwNUYswߖߞ|R՝sZ570[KS%geҸ<ZʁcPح 9yX4Ώ#j~Jڷ.9smT$9"nS@kd8Ž9&tr";Br?0݄8Sk\d`?9rA R dDAaq%LXCJ6 B8dj+uG7umsP#L]1CFk@v>p7V l2KM.^X.~4?zeu|&am$\Vw6UL }699T7emќJ 5~G4Ӣс5_ڊwz4%2;ݒV%!tG+A*" mЕ/M+KxL/k`lK{X0я?C#[1I]G%wq=3k_3+1Vҙ 6])"J:AЮM7@Hk_(φ3=IGm!C>:b^*{␀$GSIԾx\JUBdќ62rE}tbxsiLO΍̻lD(L6d=˓Ƿrnfh/W 0?w"^4K^^i 2?Wc„P͗!$*aӏ1bօ$, ,e-IYP\o{;^/ҡXF*r^{:ih 7 TsZ#Lc.7+nΚJD^҈1Y]kX$;*|bÐ<>1ʨVDWĐik ޱnUQnxԖ˯<ӠoPԫ27\kwČ.qO=.ܸڡ+4+@'S~ HR$dY~ R}oɔzHg;gYkK-7R?I)۵DO9*I7_\߀_;N7ޕ̧ɴ48RW;w̞.30͢=ܜUY&dݯkm:5ĝ!egU z魰]) gQ#9l4Y[iaD1#ƭEXa7FR0ϕLbIj~츢Ns ̤斖Ո.>-9u*UZE#Et10E9 2lg#rOf[m2.z:~ޡqz@ލoQEâ_ӈ6#[P:gk'u_PϐDӥ@R`FιgNS~g*"]") `+5ei.Bh+2ӬVM%3YE,]bSzϸڦ)Ǜ-f,LJ,\ܧ]mEç3CӴQjUBD|WgI嬂 N xi _Es@Eh8[}VhxA\ky#! ѽO.D'Jp&\̷k,1ɐ bŮ!DRS+I&8n%Kp< ^[=Cٮ,8 Y乬S[qL) u ^U-"/T F#4 }u]#[S;DD,uØY,I^d{G>$^-x5H"5._1㻾B_3P&MQ$^1U鿞ɵ/u0!v y0UGFzu7cEG Ky ) 4,xL,6,%I^hAc*3Ckd]>bP^% 1m'_2\YC|9(pB+Bs|x$#QyԌ^&d4{! wk|@ŏv//0O@a1_׆C"8 Y?^JaXwqX{%d]+Y{HR]CxJ߿Ӊ̤Su"LRF&\Gq3c X6*k -sMkϣ7z jpx TYMma*l Ƀ+)lj{ )Z.\E"JFA N8:4* <լUݽ :\zSNmo +/ϲ lIZyG)"5r,_<'n+=6G rqX)ι`].K~c$bdlqBs̥gpUAi ׆[]DF)O(|,8CܔnVTu`ȭx$ztvT̚We,N'O' ]o91o‚cC( n]ZU#}[ |Pbe:i$07϶WeV&yuxJ_+!8Ϲss_~|7QF^!(o'qv)Ι=O4M{>>󭒈bFb͹A:;]qCn+Q :LR"KU/Ձ͡)/3( dǃǨfjjwGk]&~e ³[ ?_okRB6eX2'ÈV6 Q_&4!npD#rGN 16'&Y}j{ &6=3CCZn,Dw9ĶwNSԦ# kCi fwbIG'=0wE$+ Sf80/E=8J:(g:$62M-?m! $E'/c.Sdh| e&myx|QoDVJ bkMe}aOQ%So`Ww_1-6 ʥS&jvr?ZO"y K1bdtޢ-EZ"~K7wb*%x^2tsBbn(?xA(,> xINo*n}ZHt=0Vs" 穹y.6+} J<ҸҊ ½`o#6C\B4Hн6X]V4 {'#(8JNpEDF |Fw5TY!C3 =50g#ą]ę?7&ȩBMd93^ېs߸\Jdc ]KGW 6 벾60E{.OI$"p';dv9D=RhlV(ڪwDp<^j0^b>SXhA*G]Fȵ(}z\hnm3Pߝ:{C`=a YLRjPRnɅߺV&?H 7qTsu1V-^#ŧ, A"JԀNB`@?)&`+OJYCt 1TpyvhH-VߓJXw3F^gvZFLu,U⌰Y(hB֡3+bzm yEdv0`˼\uegqU DߺU09{SbMv>K?Rdel@;'f-XG9OvCpEP+WoK aꓒъȉzs8^X8js)$|NAdKx Z 2g/Y{;IN`7P5wA d@FϢ}N:@)HdV|? "&Q[t§DG |TcMeb*q+~V#5m5_qzaopXl_kag#K^d6i,ࠣ@n,:P{{薙-1(iX .M :t ??ؚa]k̖QYo0vZiL.l3m;5) (kϩ> 7ŴmB+%NX&L>[d<4H]`akw>Yy%faƈn)\H"gtuT!0+ٍ'z̟6UIaֻJ\~mlؓExe]6qgo7[]*t߯-98SuDQCx9([ؠj\yຑJ6YCaZf`z!E_%Q ] qHﰿ%q>zb2Ⱥ ?KDR-Yn7I?O^v} d d}X/ǫ,\}Pj۸6 "Uq12INg?)uiYc_n") hV,><2̋%.Wwa4.:10G`Iיiմb'XB'6xŬz{o1 t P`hs9J[Hō.4]\s#*ɕZs&crxܨp~/ (vĀP>hӥ]L]v~/#qfQ:׬{* b"sJK>xXuc5/2ˁ=-e"PI!0DW%sCv]k_^dty'e2V 5PlmJipAmS1i;u;# ]2#Z?و=1:v2 09AXu}%_ DDQbt|κk͡/E/naxiш O> ͅȦ j<t+E;8MOfqgæ Fd9\1|(ӰgyM&3gb?p_`sPR-4:`N֊]3)b$aBhBWŐh# g&: 3b&NR@ լk-*NSxĕ?o=(~1q2[hB0ɾqd2X詡6ez(|r>@|6|c8TǑ3^@d3 v\4oELUޭ_eC9"WDo>L:dI٪>!E:/ϝsʈDz3} 13HĨLQ.sύJ)MG*tלS |-+\\Md%RSUMYg!ZLIQTnbE;#}Syi&=bGmrRhhNx.m6^/ҝH_ϻVWrEoJ1Eee節y@Z4-Vlmb_pX޻4dѷIGs_[+x!`D)#ewJ,*s`*SCۣsWi5<m#qzޛoȽ|сʛ熫kق1z:~ޡqz@ލoQEâ_ӈ6#[P:gk'u_PϐDӥ@R`FιgNS~g*"]") `+5ei.Bh+2ӬVM%3YE,]bSzϸڦ)Ǜ-f,LJ,\ܧ]mEç3CӴQjUBD|WgI嬂 N xi _Es@Eh8[}VhxA\ky#! ѽO.D'Jp&\̷k,1ɐ bŮ!DRS+I&8n%Kp< ^[=Cٮ,8 Y乬S[qL) u ^U-"/T F#4 }u]#[S;DD,uØY,I^d{G>$^-x5H"5._1㻾B_3P&MQ$^1U鿞ɵ/u0!v y0UGFzu7cEG Ky ) 4,xL,6,%I^hAc*3Ckd]>bP^% 1m'_2\YC|9(pB+Bs|x$#QyԌ^&d4{! wk|@ŏv//0O@a1_׆C"8 Y?^JaXwqX{%d]+Y{HR]CxJ߿Ӊ̤Su"LRF&\Gq3c X6*k -sMkϣ7z jpx TYMma*l Ƀ+)lj{ )Z.\E"JFA N8:4* <լUݽ :\zSNmo +/ϲ lIZyG)"5r,_<'n+=6G rqX)ι`].K~c$bdlqBs̥gpUAi ׆[]DF)O(|,8CܔnVTu`ȭx$ztvT̚We,N'O' ]o91o‚cC( n]ZU#}[ |Pbe:i$07϶WeV&yuxJ_+!8Ϲss_~|7QF^!(o'qv)Ι=O4M{>>󭒈bFb͹A:;]qCn+Q :LR"KU/Ձ͡)/3( dǃǨfjjwGk]&~e ³[ ?_okRB6eX2'ÈV6 Q_&4!npD#rGN 16'&Y}j{ &6=3CCZn,Dw9ĶwNSԦ# kCi fwbIG'=0wE$+ Sf80/E=8J:(g:$62M-?m! $E'/c.Sdh| e&myx|QoDVJ bkMe}aOQ%So`Ww_1-6 ʥS&jvr?ZO"y K1bdtޢ-EZ"~K7wb*%x^2tsBbn(?xA(,> xINo*n}ZHt=0Vs" 穹y.6+} J<ҸҊ ½`o#6C\B4Hн6X]V4 {'#(8JNpEDF |Fw5TY!C3 =50g#ą]ę?7&ȩBMd93^ېs߸\Jdc ]KGW 6 벾60E{.OI$"p';dv9D=RhlV(ڪwDp<^j0^b>SXhA*G]Fȵ(}z\hnm3Pߝ:{C`=a YLRjPRnɅߺV&?H 7qTsu1V-^#ŧ, A"JԀNB`@?)&`+OJYCt 1TpyvhH-VߓJXw3F^gvZFLu,U⌰Y(hB֡3+bzm yEdv0`˼\uegqU DߺU09{SbMv>K?Rdel@;'f-XG9OvCpEP+WoK aꓒъȉzs8^X8js)$|NAdKx Z 2g/Y{;IN`7P5wA d@FϢ}N:@)HdV|? "&Q[t§DG |TcMeb*q+~V#5m5_qzaopXl_kag#K^d6i,ࠣ@n,:P{{薙-1(iX .M :t ??ؚa]k̖QYo0vZiL.l3m;5) (kϩ> 7ŴmB+%NX&L>[d<4H]`akw>Yy%faƈn)\H"gtuT!0+ٍ'z̟6UIaֻJ\~mlؓExe]6qgo7[]*t߯-98SuDQCx9([ؠj\yຑJ6YCaZf`z!E_%Q ] qHﰿ%q>zb2Ⱥ ?KDR-Yn7I?O^v} d d}X/ǫ,\}Pj۸6 "Uq12INg?)uiYc_n") hV,><2̋%.Wwa4.:10G`Iיiմb'XB'6xŬz{o1 t P`hs9J[Hō.4]\s#*ɕZs&crxܨp~/ (vĀP>hӥ]L]v~/#qfQ:׬{* b"sJK>xXuc5/2ˁ=-e"PI!0DW%sCv]k_^dty'e2V 5PlmJipAmS1i;u;# ]2#Z?و=1:v2 09AXu}%_ DDQbt|κk͡/E/naxiш O> ͅȦ j<t+E;8MOfqgæ Fd9\1|(ӰgyM&3gb?p_`sPR-4:`N֊]3)b$aBhBWŐh# g&: 3b&NR@ լk-*NSxĕ?o=(~1q2[hB0ɾqd2X詡6ez(|r>@|6|c8TǑ3^@d3 v\4oELUޭ_eC9"WDo>L:dI٪>!E:/ϝsʈDz3} 13HĨLQ.sύJ)MG*tלS |-+\\Md%RSUMYg!ZLIQTnbE;#}Syi&=bGmrRhhNx.m6^/ҝH_ϻVWrEoJ1Eee節y@Z4-Vlmb_pX޻4dѷIGs_[+x!`D)#ewJ,*s`*SCۣsWi5<m#qzޛoȽ|сʛ熫kق1z:~ޡqz@ލoQEâ_ӈ6#[P:gk'u_PϐDӥ@R`FιgNS~g*"]") `+5ei.Bh+2ӬVM%3YE,]bSzϸڦ)Ǜ-f,LJ,\ܧ]mEç3CӴQjUBD|WgI嬂 N xi _Es@Eh8[}VhxA\ky#! ѽO.D'Jp&\̷k,1ɐ bŮ!DRS+I&8n%Kp< ^[=Cٮ,8 Y乬S[qL) u ^U-"/T F#4 }u]#[S;DD,uØY,I^d{G>$^-x5H"5._1㻾B_3P&MQ$^1U鿞ɵ/u0!v y0UGFzu7cEG Ky ) 4,xL,6,%I^hAc*3Ckd]>bP^% 1m'_2\YC|9(pB+Bs|x$#QyԌ^&d4{! wk|@ŏv//0O@a1_׆C"8 Y?^JaXwqX{%d]+Y{HR]CxJ߿Ӊ̤Su"LRF&\Gq3c X6*k -sMkϣ7z jpx TYMma*l Ƀ+)lj{ )Z.\E"JFA N8:4* <լUݽ :\zSNmo +/ϲ lIZyG)"5r,_<'n+=6G rqX)ι`].K~c$bdlqBs̥gpUAi ׆[]DF)O(|,8CܔnVTu`ȭx$ztvT̚We,N'O' ]o91o‚cC( n]ZU#}[ |Pbe:i$07϶WeV&yuxJ_+!8Ϲss_~|7QF^!(o'qv)Ι=O4M{>>󭒈bFb͹A:;]qCn+Q :LR"KU/Ձ͡)/3( dǃǨfjjwGk]&~e ³[ ?_okRB6eX2'ÈV6 Q_&4!npD#rGN 16'&Y}j{ &6=3CCZn,Dw9ĶwNSԦ# kCi fwbIG'=0wE$+ Sf80/E=8J:(g:$62M-?m! $E'/c.Sdh| e&myx|QoDVJ bkMe}aOQ%So`Ww_1-6 ʥS&jvr?ZO"y K1bdtޢ-EZ"~K7wb*%x^2tsBbn(?xA(,> xINo*n}ZHt=0Vs" 穹y.6+} J<ҸҊ ½`o#6C\B4Hн6X]V4 {'#(8JNpEDF |Fw5TY!C3 =50g#ą]ę?7&ȩBMd93^ېs߸\Jdc ]KGW 6 벾60E{.OI$"p';dv9D=RhlV(ڪwDp<^j0^b>SXhA*G]Fȵ(}z\hnm3Pߝ:{C`=a YLRjPRnɅߺV&?H 7qTsu1V-^#ŧ, A"JԀNB`@?)&`+OJYCt 1TpyvhH-VߓJXw3F^gvZFLu,U⌰Y(hB֡3+bzm yEdv0`˼\uegqU DߺU09{SbMv>K?Rdel@;'f-XG9OvCpEP+WoK aꓒъȉzs8^X8js)$|NAdKx Z 2g/Y{;IN`7P5wA d@FϢ}N:@)HdV|? "&Q[t§DG |TcMeb*q+~V#5m5_qzaopXl_kag#K^d6i,ࠣ@n,:P{{薙-1(iX .M :t ??ؚa]k̖QYo0vZiL.l3m;5) (kϩ> 7ŴmB+%NX&L>[d<4H]`akw>Yy%faƈn)\H"gtuT!0+ٍ'z̟6UIaֻJ\~mlؓExe]6qgo7[]*t߯-98SuDQCx9([ؠj\yຑJ6YCaZf`z!E_%Q ] qHﰿ%q>zb2Ⱥ ?KDR-Yn7I?O^v} d d}X/ǫ,\}Pj۸6 "Uq12INg?)uiYc_n") hV,><2̋%.Wwa4.:10G`Iיiմb'XB'6xŬz{o1 t P`hs9J[Hō.4]\s#*ɕZs&crxܨp~/ (vĀP>hӥ]L]v~/#qfQ:׬{* b"sJK>xXuc5/2ˁ=-e"PI!0DW%sCv]k_^dty'e2V 5PlmJipAmS1i;u;# ]2#Z?و=1:v2 09AXu}%_ DDQbt|κk͡/E/naxiш O> ͅȦ j<t+E;8MOfqgæ Fd9\1|(ӰgyM&3gb?p_`sPR-4:`N֊]3)b$aBhBWŐh# g&: 3b&NR@ լk-*NSxĕ?o=(~1q2[hB0ɾqd2X詡6ez(|r>@|6|c8TǑ3^@d3 v\4oELUޭ_eC9"WDo>L:dI٪>!E:/ϝsʈDz3} 13HĨLQ.sύJ)MG*tלS |-+\\Md%RSUMYg!ZLIQTnbE;#}Syi&=bGmrRhhNx.m6^/ҝH_ϻVWrEoJ1Eee節y@Z4-Vlmb_pX޻4dѷIGs_[+x!`D)#ewJ,*s`*SCۣsWi5<m#qzޛoȽ|сʛ熫kق1oW=7-z} 4; (2:޿.Yo\=ޛoOasy%M{b ZbQ:Ϯ㿂4Bs -WE%xu,e1<{YlbXԹ/BD qs`qQmdڒL ,}+s/眈WbwnRG>jJ\Ͷ,Nz_V^ЗӐ;?޲b#-% }IXuzӕ|oWG j#>x<^BP:߮?So92B-#M#u[.ٮ|)D=>~̼c[F8Q9QO6^WUa"gm. K R)-ofZ |i&sԃ o!EhE$^vʚKhٰMNX x"E}z"D$h[ *heUf6VtMX1,Q;տ]`pHx3 cM/xMxQ㓆y@H2 "K EUi)іࡕ#xsh*0<慱h:b=9R3PD5"UF4JϕypI- v#H⮸>{Ok|zF#N// gzylFdY`%6]7OLπe뱈񸧱oCJ *R ]td#GB;3Y[zX6O3̍+-XjQPYZ+8uy/vzɖI|;0<ȉr4Q;@ P57nhe' : wZ$v8AEvG[ՕB9W6 [/0ܛ#8QyP,$1  -B!J ]FF%3;ۖ3A˔\5 JQG5gU6r p$1l7 BPAf[S77e\gc#UByFUR {6\AZEpC70SBȳmpf;=}Az3jO&WrJ) YUF lar& KKG,Ȩٲ1ΟlPip8CU-3ƌMQ404oHNc"4uE`h²r1-]p@8́S&)TMwˋz6߬Rf2e:IQYCYXq׫a0W]uoAaM۫W3]tē@`UC5ާ#uT+"2:XRڊW Q S= Z/$?sHJn&ՠXfyϘ3B$BQpLgN ,}Yi(i(+U aN N맣SP51\gn*h['ˉUF Ή<#٠XJ3;5@Cxƻ6gzdZ[ͭD\|ټF}r&S6G lŦ*n"d׻ lO?඀ ,_{I9 abw~7$b\~c^4L*7QAYfPnCo~&r3dbX>^2 ȐxX:! 4V =\O,MV ]a'"ob)[Y۞2Ƭs0Eb8ɐQw>„=[+_b?˩^KiAl(%xb֛׈%> F*F1 jEQ(pnvwi겆p6>0YÐ1Jϋ@X')%v\W#N;~yt Xy&i~ YSf}"q<4%tT&f/ԺTv"SM;#a+xWEQBjJܕ AXIq/ay'i-#Z{z9ӟYL?׊y ?`65owuAc Zl$Gfҹp PqX ֠#7ؖgAGWJA$<^[۵XtooCeK͠pͦ:锁C_@rlcpy"-,{C,/).1F"d;bDق&,(5|EvNF>`ᥚ-.di40[[S:k/[ cEXKKK8{Yi03v#t<_ok>5W% KlK6Ym7Y 8Y:|g#aBižm$h,‡i}Wg}W쭿mt_[oۭؿ}:o]Ut=\\_IۥSegt?pʗ?mKmoۢ?E?mgW/ۧXרԂ$."uj~)h'ϩNݮSRV^ ΓrDQ E\ ']6Y-*Ԍ12 cn\'={kq>iu!!+ndd|P( ӗO/vXϨhˋkeŭV(X*e6=/[2 c%ʢ%KfB Qڨ²ECzV8/gKqK}EF@!juH#?4dSF9SoTVi[VM,7S% UŋEa'.4ؒ# 4A1k' )H o< _<yY"zg '=K : =:YWҳIxp{$D<n1һAbE[P5K~ 5q>mťq9.F*j*7yS F+v&?nW{ Mӝ%>RZK7uv\Q*BhR 0pWw<zk~H {)Gȯ;\x89 (c#bTgcAʩ]T ~. J/Z$}׈bV>Rmy!1g,w [X97C8 MS@OuJɾiŒTO_8=M<@xb1N_ X6<YcX6r4=+8Aq$Uz:MzYJPܾ!./jh,^0CxC'w`Ɣ#UVvg,1GO*/pZ7FSvZԚ.[o5rSH(-@ucoe`ӊ_PS{`FlQ[kAC*U. e$P<[M6StcgU;mV# 8̓3?&X4džK4KoS} ilUtQkx%}Շ/cY4ygKL_S+t 򑘼kx :wݵLķkr':k^YE fO6$i O//^@njXBn_3sx937*9"UF9Ѹw ÝC+Lmgrz4jKBc}TeRn0#U.?qDgC0R=POx#.%mKV|C<,kQ>2a5G3 ȎWbmsaeu N+JP&C}R t X#kcǐT]6۩^wH 8FQڀ~ :4_Kx +ӯƒw#).~eD)-0I=RtX93xSH`Vj4p5u͑O7G?˓[^dJJew$umm*{Ւ-c>2oLPM~o#mH3FL܋ʂp^c-3^Eֈ3+mݺ0qf: XVpO?E?5 &/Ct.X_ y<Gܑb WDUfʐ8=̝i*Ƥ6t&b0m U!E 3TIMK4u3SJ*Hp΍ΧlGUbr9 D'X3J1y$t%r%-t#eWoB8R=DJß{"Sq Eu [@"E6C+ødꭣh,4آ~]Ѽq6@[ν,tSFn\6Jn J@/l' M>Esܟݺ u]"qX=H*λ̲g!U;\bM pY){F6L/hA%  &j䞗Sg&=0䴪K|G$z c$ktڱrMb\' 1vW $-m1\ d޸{W')F>>c31??D.\@T9kⱔYrr^{GVyDb(rnA(dl]"E}yv!s=qc#]$~o! 6Vq)G;\k3;x~V? i hC%@C;L=ߍ:R&+( O{?^F01nvl X`FOlay,`ܝQ-e=)׽&tCkC!#fN gH55QwfsyqU%ZSca$<^r_"=1 jɭ>ρHXk}\:6=d2kI::55~ ۠ ć293XB\D~v9ҭ+{$zI%gu*sm&UKdf3 poeT'cQyv'ݤԄOXQ emay+i$cq(&AK#r]O3DL {}.u*灴1pm=. f&FZ>9ܻ'd3⊡ԋ#άl:W>9 1uFfdlF.>jƺ_(sjpG;0paÀ9#oTN #/2͒յ{͉Ή;=qJJ0ZL s_KiԜ >=/Vj7֩~'!W{NM,$cϼQ?3ٻ㿨m"ΈBGdN%'$٬H@ޑk+%Bv5]>Uibrm"ꐙPsY*#2I5*ZI1SEmK4dMljB~pDmj ?ZBMCmeΪW+\z-9~Q0qɘ qރ᲻;|&#u2OqV@4 $Ic<[ SqL,/炸7QC :FhNI(A(_:VzS0vp)twkCp0(KwM 8{c.*–Cz;2"y%[; x{s>?p%i9DLdW( 5ރo(M]g_vl]E YnMLrvw1I%fojb$͹k` bqJln|X3)5٢ѰB+QRbQ I!]ә\j!V`Xp9o'Iw*Ec~8EKmˌo8[+PO5v w#㑧] 'ν>UOqѿgZà ePS`>k v dJF^'=ʡCyte[[ VW`#'ɎmY:.eTCJ*׌L0oI-c%hzZ?Ǵ"6)Ť'^yBnDX 啳x.12۠LXVOxHY2Ge.؁I<$𙣛N}=I1[QT3sKkĿӛ;0VOK |G&^cQY >&Oae|VP PpҜVmI i7ͯ8 M;g[J{%?;Nshiî.U#,4Mf}p+W@GtgmXRٺ p Ke\@~F[T,6Ef=M{blݐlMXֱ8SQ_ƈ)0k!Gm&!$ގJ`feE:-@Jj18$/bG;yXH2Gn}Nh,7g%us GjSSʍpvpJŒ*joA~b4 c}cPVlPXc6Prڬ6>,G3 F2r{̨=;"yi:Pn{cl ^XN2y"T\lkhNyֹw'e@ !y4q;Cї$ I#|v:P,9;D ۮc fkg,P[-KTB=qߪZ)F" Bճxc2Q]?DmrM@ 7U"EPiY]Pl9Rej+dFbj[x0`nFv]Qu jB ,%}mb?}C| &8Q9mkB)c { +֥Fem2N9_ŵk4]m Z/?+1&˒ÐN)vĦs_ir,vLA{r}]Vkpl3ӆ2W Xe\Hxa@w1<iQ] P{&A&ÑCTPȡXs6뙶-ȕ^ b>Pm#)7 4`3fLL\!sк,.^-zq3\ԙE\vN}w#/eP^!凭+$aĚר6dL" ]m@sx H-^8"k;+Hx5?ӄ-FQJzQQ+ɿo,pz$_L ) {j )@C;JO+2_A66J>#q{9D+vN)9WJ "ufsH8UE+r bȐUho,HS"sjIԱ;nBwJu#|]qE5E$u%$M~63+QqEn0Oh5fi\<}B,uѫ"}"3ɋ CH졭;`T{{O/srl_E%g o x߅u]'!Vẻx~~,n`)iDɠyngj1yJsm+O!kMn`h\HUVU]|D0^lj=&(<"%VhyL]q(OMlf])FDz!9|e,@Egz>2W뗊d8Yk6n!DrX58"`٤ ɸ'9$A}ӝ\Yx qzgy`BX)c+\&WX,+)4_o5wM@*9CGyEE t d#/di wl/9_'{f"bSDA˵f. uĄhgv'=*mѣ *?B@b>\i[87rgD/ pC^ɴ;*_Ե4d^PW\xEEcxtȩ_uijوPr'~HxXssz2 UjHPu~u7]P[ ,<>z]įvj#֡ ZwVL +i~ p8lmeHJJ+AUVuP֞H 4=Y2ߌ T%! :\տjdgFR`!'TAB8/[x;iSLŒ*MkgJMM_+~XǼ< a_@WQVg܄;'Y; gTmrLB|q+`6\]s;$U\jkuך~Sty^mwM.ЧD|0e(?R?2"0XY1HYw;#ei~ 4~F-6H|mr뛉gfSPo{vMܤ\<줖W&Csn^8ďKey|6Lt"զPT GGբ g!&Mq3@d~V ?Mر"(" `)`RP+`AߖMӇV@ E}|g)fN]cv8P62bWwg`3B}1 v+@׻N{nM9s@R2'?ͳ4:pEE-d3~>Tyn{Ms 1 I7M:!YvZL,ƍCzoL]A0)g݄P5yc$A-f^i`PpT/ګzMO}߹DDXi6%TB?9p*bW4z48gv,U_ F1SLܼS)k}C ]Qx't~?OP[j`` a4/6TT'H ߀Ǿ@!&djͷφ9cBҕMXJ t{юi3)@GE8<%% ])q` Sٙ$:ps^oB#^4}RVi)@\qLv,2ʽr@AvML.1Q$$P*5TD$n56CwW%$:DBD+ߝ\RLС@tH!x?4BLbd2 ,$$WɠMOی H$DxQ”J"9_1. /eE͵{ŭ2K6@K/*ʾ#IdqӘ:~ڢ>BvD< ,=biu:q w.\]?9m/JH%-@E!#X\ndg2r0ɂגUy=='ۤ b^1'L$MR70j mWřRD^G" "C²۽׭+"]mN% Ǧa8D.]ML,TTЌc'5i@, 2,swloqr~sU)P\: o%L|5rޢЎn[*ϟU?"'Oec6 ܉">=qpUi uj(7slmDisoA!UR[f=pGmb/ Õd=XZ%HGbIK'w!mMi=QA=RY\W/[r=5P帅*YYmN,̅^yb|H;bXRQM6Nbr۫KlsE"&߳-g!k;u X~CJA4ȡz zz.| >5Sǁ@knivTdԬ1^`AV!Dqn2j.OwպcT~֟ծ 7i+ h^ޯ)2}s b_żtI7%MSQO;DA.j;2q^`0+| S|Njl.t=Bw͒j$ܡ/9iCO:[]ٞN^ }jgQSv@uudN1}u1bM,N9&@kf*ߚp,r2,oХ|QħUq>;5:r I_u;mú)z(C[_6M" AÁ,PY wu-Xc #Q\0x~hMM:Efvn=/ !*۽k^ܺX,ʷӚwqt;{N k2ݴ?ـ@nŤ*eΡkQ4g?[,j/w,4bN+4E*E; ^ !gEs+cP9|880]aN^ XEvuPjU%BBcp9C?4h;DM#o7\U;ϰ9 U%fc]RMkh'ϩNݮSRV^ ΓrDQ E\ ']6Y-*Ԍ12 cn\'={kq>iu!!+ndd|P( ӗO/vXϨhˋkeŭV(X*e6=/[2 c%ʢ%KfB Qڨ²ECzV8/gKqK}EF@!juH#?4dSF9SoTVi[VM,7S% UŋEa'.4ؒ# 4A1k' )H o< _<yY"zg '=K : =:YWҳIxp{$D<n1һAbE[P5K~ 5q>mťq9.F*j*7yS F+v&?nW{ Mӝ%>RZK7uv\Q*BhR 0pWw<zk~H {)Gȯ;\x89 (c#bTgcAʩ]T ~. J/Z$}׈bV>Rmy!1g,w [X97C8 MS@OuJɾiŒTO_8=M<@xb1N_ X6<YcX6r4=+8Aq$Uz:MzYJPܾ!./jh,^0CxC'w`Ɣ#UVvg,1GO*/pZ7FSvZԚ.[o5rSH(-@ucoe`ӊ_PS{`FlQ[kAC*U. e$P<[M6StcgU;mV# 8̓3?&X4džK4KoS} ilUtQkx%}Շ/cY4ygKL_S+t 򑘼kx :wݵLķkr':k^YE fO6$i O//^@njXBn_3sx937*9"UF9Ѹw ÝC+Lmgrz4jKBc}TeRn0#U.?qDgC0R=POx#.%mKV|C<,kQ>2a5G3 ȎWbmsaeu N+JP&C}R t X#kcǐT]6۩^wH 8FQڀ~ :4_Kx +ӯƒw#).~eD)-0I=RtX93xSH`Vj4p5u͑O7G?˓[^dJJew$umm*{Ւ-c>2oLPM~o#mH3FL܋ʂp^c-3^Eֈ3+mݺ0qf: XVpO?E?5 &/Ct.X_ y<Gܑb WDUfʐ8=̝i*Ƥ6t&b0m U!E 3TIMK4u3SJ*Hp΍ΧlGUbr9 D'X3J1y$t%r%-t#eWoB8R=DJß{"Sq Eu [@"E6C+ødꭣh,4آ~]Ѽq6@[ν,tSFn\6Jn J@/l' M>Esܟݺ u]"qX=H*λ̲g!U;\bM pY){F6L/hA%  &j䞗Sg&=0䴪K|G$z c$ktڱrMb\' 1vW $-m1\ d޸{W')F>>c31??D.\@T9kⱔYrr^{GVyDb(rnA(dl]"E}yv!s=qc#]$~o! 6Vq)G;\k3;x~V? i hC%@C;L=ߍ:R&+( O{?^F01nvl X`FOlay,`ܝQ-e=)׽&tCkC!#fN gH55QwfsyqU%ZSca$<^r_"=1 jɭ>ρHXk}\:6=d2kI::55~ ۠ ć293XB\D~v9ҭ+{$zI%gu*sm&UKdf3 poeT'cQyv'ݤԄOXQ emay+i$cq(&AK#r]O3DL {}.u*灴1pm=. f&FZ>9ܻ'd3⊡ԋ#άl:W>9 1uFfdlF.>jƺ_(sjpG;0paÀ9#oTN #/2͒յ{͉Ή;=qJJ0ZL s_KiԜ >=/Vj7֩~'!W{NM,$cϼQ?3ٻ㿨m"ΈBGdN%'$٬H@ޑk+%Bv5]>Uibrm"ꐙPsY*#2I5*ZI1SEmK4dMljB~pDmj ?ZBMCmeΪW+\z-9~Q0qɘ qރ᲻;|&#u2OqV@4 $Ic<[ SqL,/炸7QC :FhNI(A(_:VzS0vp)twkCp0(KwM 8{c.*–Cz;2"y%[; x{s>?p%i9DLdW( 5ރo(M]g_vl]E YnMLrvw1I%fojb$͹k` bqJln|X3)5٢ѰB+QRbQ I!]ә\j!V`Xp9o'Iw*Ec~8EKmˌo8[+PO5v w#㑧] 'ν>UOqѿgZà ePS`>k v dJF^'=ʡCyte[[ VW`#'ɎmY:.eTCJ*׌L0oI-c%hzZ?Ǵ"6)Ť'^yBnDX 啳x.12۠LXVOxHY2Ge.؁I<$𙣛N}=I1[QT3sKkĿӛ;0VOK |G&^cQY >&Oae|VP PpҜVmI i7ͯ8 M;g[J{%?;Nshiî.U#,4Mf}p+W@GtgmXRٺ p Ke\@~F[T,6Ef=M{blݐlMXֱ8SQ_ƈ)0k!Gm&!$ގJ`feE:-@Jj18$/bG;yXH2Gn}Nh,7g%us GjSSʍpvpJŒ*joA~b4 c}cPVlPXc6Prڬ6>,G3 F2r{̨=;"yi:Pn{cl ^XN2y"T\lkhNyֹw'e@ !y4q;Cї$ I#|v:P,9;D ۮc fkg,P[-KTB=qߪZ)F" Bճxc2Q]?DmrM@ 7U"EPiY]Pl9Rej+dFbj[x0`nFv]Qu jB ,%}mb?}C| &8Q9mkB)c { +֥Fem2N9_ŵk4]m Z/?+1&˒ÐN)vĦs_ir,vLA{r}]Vkpl3ӆ2W Xe\Hxa@w1<iQ] P{&A&ÑCTPȡXs6뙶-ȕ^ b>Pm#)7 4`3fLL\!sк,.^-zq3\ԙE\vN}w#/eP^!凭+$aĚר6dL" ]m@sx H-^8"k;+Hx5?ӄ-FQJzQQ+ɿo,pz$_L ) {j )@C;JO+2_A66J>#q{9D+vN)9WJ "ufsH8UE+r bȐUho,HS"sjIԱ;nBwJu#|]qE5E$u%$M~63+QqEn0Oh5fi\<}B,uѫ"}"3ɋ CH졭;`T{{O/srl_E%g o x߅u]'!Vẻx~~,n`)iDɠyngj1yJsm+O!kMn`h\HUVU]|D0^lj=&(<"%VhyL]q(OMlf])FDz!9|e,@Egz>2W뗊d8Yk6n!DrX58"`٤ ɸ'9$A}ӝ\Yx qzgy`BX)c+\&WX,+)4_o5wM@*9CGyEE t d#/di wl/9_'{f"bSDA˵f. uĄhgv'=*mѣ *?B@b>\i[87rgD/ pC^ɴ;*_Ե4d^PW\xEEcxtȩ_uijوPr'~HxXssz2 UjHPu~u7]P[ ,<>z]įvj#֡ ZwVL +i~ p8lmeHJJ+AUVuP֞H 4=Y2ߌ T%! :\տjdgFR`!'TAB8/[x;iSLŒ*MkgJMM_+~XǼ< a_@WQVg܄;'Y; gTmrLB|q+`6\]s;$U\jkuך~Sty^mwM.ЧD|0e(?R?2"0XY1HYw;#ei~ 4~F-6H|mr뛉gfSPo{vMܤ\<줖W&Csn^8ďKey|6Lt"զPT GGբ g!&Mq3@d~V ?Mر"(" `)`RP+`AߖMӇV@ E}|g)fN]cv8P62bWwg`3B}1 v+@׻N{nM9s@R2'?ͳ4:pEE-d3~>Tyn{Ms 1 I7M:!YvZL,ƍCzoL]A0)g݄P5yc$A-f^i`PpT/ګzMO}߹DDXi6%TB?9p*bW4z48gv,U_ F1SLܼS)k}C ]Qx't~?OP[j`` a4/6TT'H ߀Ǿ@!&djͷφ9cBҕMXJ t{юi3)@GE8<%% ])q` Sٙ$:ps^oB#^4}RVi)@\qLv,2ʽr@AvML.1Q$$P*5TD$n56CwW%$:DBD+ߝ\RLС@tH!x?4BLbd2 ,$$WɠMOی H$DxQ”J"9_1. /eE͵{ŭ2K6@K/*ʾ#IdqӘ:~ڢ>BvD< ,=biu:q w.\]?9m/JH%-@E!#X\ndg2r0ɂגUy=='ۤ b^1'L$MR70j mWřRD^G" "C²۽׭+"]mN% Ǧa8D.]ML,TTЌc'5i@, 2,swloqr~sU)P\: o%L|5rޢЎn[*ϟU?"'Oec6 ܉">=qpUi uj(7slmDisoA!UR[f=pGmb/ Õd=XZ%HGbIK'w!mMi=QA=RY\W/[r=5P帅*YYmN,̅^yb|H;bXRQM6Nbr۫KlsE"&߳-g!k;u X~CJA4ȡz zz.| >5Sǁ@knivTdԬ1^`AV!Dqn2j.OwպcT~֟ծ 7i+ h^ޯ)2}s b_żtI7%MSQO;DA.j;2q^`0+| S|Njl.t=Bw͒j$ܡ/9iCO:[]ٞN^ }jgQSv@uudN1}u1bM,N9&@kf*ߚp,r2,oХ|QħUq>;5:r I_u;mú)z(C[_6M" AÁ,PY wu-Xc #Q\0x~hMM:Efvn=/ !*۽k^ܺX,ʷӚwqt;{N k2ݴ?ـ@nŤ*eΡkQ4g?[,j/w,4bN+4E*E; ^ !gEs+cP9|880]aN^ XEvuPjU%BBcp9C?4h;DM#o7\U;ϰ9 U%fc]RMkh'ϩNݮSRV^ ΓrDQ E\ ']6Y-*Ԍ12 cn\'={kq>iu!!+ndd|P( ӗO/vXϨhˋkeŭV(X*e6=/[2 c%ʢ%KfB Qڨ²ECzV8/gKqK}EF@!juH#?4dSF9SoTVi[VM,7S% UŋEa'.4ؒ# 4A1k' )H o< _<yY"zg '=K : =:YWҳIxp{$D<n1һAbE[P5K~ 5q>mťq9.F*j*7yS F+v&?nW{ Mӝ%>RZK7uv\Q*BhR 0pWw<zk~H {)Gȯ;\x89 (c#bTgcAʩ]T ~. J/Z$}׈bV>Rmy!1g,w [X97C8 MS@OuJɾiŒTO_8=M<@xb1N_ X6<YcX6r4=+8Aq$Uz:MzYJPܾ!./jh,^0CxC'w`Ɣ#UVvg,1GO*/pZ7FSvZԚ.[o5rSH(-@ucoe`ӊ_PS{`FlQ[kAC*U. e$P<[M6StcgU;mV# 8̓3?&X4džK4KoS} ilUtQkx%}Շ/cY4ygKL_S+t 򑘼kx :wݵLķkr':k^YE fO6$i O//^@njXBn_3sx937*9"UF9Ѹw ÝC+Lmgrz4jKBc}TeRn0#U.?qDgC0R=POx#.%mKV|C<,kQ>2a5G3 ȎWbmsaeu N+JP&C}R t X#kcǐT]6۩^wH 8FQڀ~ :4_Kx +ӯƒw#).~eD)-0I=RtX93xSH`Vj4p5u͑O7G?˓[^dJJew$umm*{Ւ-c>2oLPM~o#mH3FL܋ʂp^c-3^Eֈ3+mݺ0qf: XVpO?E?5 &/Ct.X_ y<Gܑb WDUfʐ8=̝i*Ƥ6t&b0m U!E 3TIMK4u3SJ*Hp΍ΧlGUbr9 D'X3J1y$t%r%-t#eWoB8R=DJß{"Sq Eu [@"E6C+ødꭣh,4آ~]Ѽq6@[ν,tSFn\6Jn J@/l' M>Esܟݺ u]"qX=H*λ̲g!U;\bM pY){F6L/hA%  &j䞗Sg&=0䴪K|G$z c$ktڱrMb\' 1vW $-m1\ d޸{W')F>>c31??D.\@T9kⱔYrr^{GVyDb(rnA(dl]"E}yv!s=qc#]$~o! 6Vq)G;\k3;x~V? i hC%@C;L=ߍ:R&+( O{?^F01nvl X`FOlay,`ܝQ-e=)׽&tCkC!#fN gH55QwfsyqU%ZSca$<^r_"=1 jɭ>ρHXk}\:6=d2kI::55~ ۠ ć293XB\D~v9ҭ+{$zI%gu*sm&UKdf3 poeT'cQyv'ݤԄOXQ emay+i$cq(&AK#r]O3DL {}.u*灴1pm=. f&FZ>9ܻ'd3⊡ԋ#άl:W>9 1uFfdlF.>jƺ_(sjpG;0paÀ9#oTN #/2͒յ{͉Ή;=qJJ0ZL s_KiԜ >=/Vj7֩~'!W{NM,$cϼQ?3ٻ㿨m"ΈBGdN%'$٬H@ޑk+%Bv5]>Uibrm"ꐙPsY*#2I5*ZI1SEmK4dMljB~pDmj ?ZBMCmeΪW+\z-9~Q0qɘ qރ᲻;|&#u2OqV@4 $Ic<[ SqL,/炸7QC :FhNI(A(_:VzS0vp)twkCp0(KwM 8{c.*–Cz;2"y%[; x{s>?p%i9DLdW( 5ރo(M]g_vl]E YnMLrvw1I%fojb$͹k` bqJln|X3)5٢ѰB+QRbQ I!]ә\j!V`Xp9o'Iw*Ec~8EKmˌo8[+PO5v w#㑧] 'ν>UOqѿgZà ePS`>k v dJF^'=ʡCyte[[ VW`#'ɎmY:.eTCJ*׌L0oI-c%hzZ?Ǵ"6)Ť'^yBnDX 啳x.12۠LXVOxHY2Ge.؁I<$𙣛N}=I1[QT3sKkĿӛ;0VOK |G&^cQY >&Oae|VP PpҜVmI i7ͯ8 M;g[J{%?;Nshiî.U#,4Mf}p+W@GtgmXRٺ p Ke\@~F[T,6Ef=M{blݐlMXֱ8SQ_ƈ)0k!Gm&!$ގJ`feE:-@Jj18$/bG;yXH2Gn}Nh,7g%us GjSSʍpvpJŒ*joA~b4 c}cPVlPXc6Prڬ6>,G3 F2r{̨=;"yi:Pn{cl ^XN2y"T\lkhNyֹw'e@ !y4q;Cї$ I#|v:P,9;D ۮc fkg,P[-KTB=qߪZ)F" Bճxc2Q]?DmrM@ 7U"EPiY]Pl9Rej+dFbj[x0`nFv]Qu jB ,%}mb?}C| &8Q9mkB)c { +֥Fem2N9_ŵk4]m Z/?+1&˒ÐN)vĦs_ir,vLA{r}]Vkpl3ӆ2W Xe\Hxa@w1<iQ] P{&A&ÑCTPȡXs6뙶-ȕ^ b>Pm#)7 4`3fLL\!sк,.^-zq3\ԙE\vN}w#/eP^!凭+$aĚר6dL" ]m@sx H-^8"k;+Hx5?ӄ-FQJzQQ+ɿo,pz$_L ) {j )@C;JO+2_A66J>#q{9D+vN)9WJ "ufsH8UE+r bȐUho,HS"sjIԱ;nBwJu#|]qE5E$u%$M~63+QqEn0Oh5fi\<}B,uѫ"}"3ɋ CH졭;`T{{O/srl_E%g o x߅u]'!Vẻx~~,n`)iDɠyngj1yJsm+O!kMn`h\HUVU]|D0^lj=&(<"%VhyL]q(OMlf])FDz!9|e,@Egz>2W뗊d8Yk6n!DrX58"`٤ ɸ'9$A}ӝ\Yx qzgy`BX)c+\&WX,+)4_o5wM@*9CGyEE t d#/di wl/9_'{f"bSDA˵f. uĄhgv'=*mѣ *?B@b>\i[87rgD/ pC^ɴ;*_Ե4d^PW\xEEcxtȩ_uijوPr'~HxXssz2 UjHPu~u7]P[ ,<>z]įvj#֡ ZwVL +i~ p8lmeHJJ+AUVuP֞H 4=Y2ߌ T%! :\տjdgFR`!'TAB8/[x;iSLŒ*MkgJMM_+~XǼ< a_@WQVg܄;'Y; gTmrLB|q+`6\]s;$U\jkuך~Sty^mwM.ЧD|0e(?R?2"0XY1HYw;#ei~ 4~F-6H|mr뛉gfSPo{vMܤ\<줖W&Csn^8ďKey|6Lt"զPT GGբ g!&Mq3@d~V ?Mر"(" `)`RP+`AߖMӇV@ E}|g)fN]cv8P62bWwg`3B}1 v+@׻N{nM9s@R2'?ͳ4:pEE-d3~>Tyn{Ms 1 I7M:!YvZL,ƍCzoL]A0)g݄P5yc$A-f^i`PpT/ګzMO}߹DDXi6%TB?9p*bW4z48gv,U_ F1SLܼS)k}C ]Qx't~?OP[j`` a4/6TT'H ߀Ǿ@!&djͷφ9cBҕMXJ t{юi3)@GE8<%% ])q` Sٙ$:ps^oB#^4}RVi)@\qLv,2ʽr@AvML.1Q$$P*5TD$n56CwW%$:DBD+ߝ\RLС@tH!x?4BLbd2 ,$$WɠMOی H$DxQ”J"9_1. /eE͵{ŭ2K6@K/*ʾ#IdqӘ:~ڢ>BvD< ,=biu:q w.\]?9m/JH%-@E!#X\ndg2r0ɂגUy=='ۤ b^1'L$MR70j mWřRD^G" "C²۽׭+"]mN% Ǧa8D.]ML,TTЌc'5i@, 2,swloqr~sU)P\: o%L|5rޢЎn[*ϟU?"'Oec6 ܉">=qpUi uj(7slmDisoA!UR[f=pGmb/ Õd=XZ%HGbIK'w!mMi=QA=RY\W/[r=5P帅*YYmN,̅^yb|H;bXRQM6Nbr۫KlsE"&߳-g!k;u X~CJA4ȡz zz.| >5Sǁ@knivTdԬ1^`AV!Dqn2j.OwպcT~֟ծ 7i+ h^ޯ)2}s b_żtI7%MSQO;DA.j;2q^`0+| S|Njl.t=Bw͒j$ܡ/9iCO:[]ٞN^ }jgQSv@uudN1}u1bM,N9&@kf*ߚp,r2,oХ|QħUq>;5:r I_u;mú)z(C[_6M" AÁ,PY wu-Xc #Q\0x~hMM:Efvn=/ !*۽k^ܺX,ʷӚwqt;{N k2ݴ?ـ@nŤ*eΡkQ4g?[,j/w,4bN+4E*E; ^ !gEs+cP9|880]aN^ XEvuPjU%BBcp9C?4h;DM#o7\U;ϰ9 U%fc]RMk{Zb߾ӇTZ_V~kw5;ʔ #k-qc@Ó vJ4bt4JQG@>x)+rH 5YK{z@[a̾Hik;v: 6w7wY,kAcla`/PDNF+3aT5-ܧZʯ8ǹU GM̃p+TYQied^l`+Qʼn썲 U[F$"}{4-NMWpY>ږa 5>CXô%CQ SГ<:.*O>O5Cn0_=S2X^b@Z khi|1RMkLsG&MKPS jP1,:,H#e25L alop( aOt*Se|d_p\CSRSa$ rn)*q-&:8"脻sw602׾ "sW,bnG*ԓSg%wuTV>S!փ*ڈD^ۓ^`n̚1ۆv6ZvTȝoCi>a)oN# e]Ow{{*zƅE":q;/!ZmdZR-i}a-N@yЩPuS|0b/߼J:ק^*sHhcyd0Op埕eJ4 ǹPg`yx YY ĉ?(ţ-EGco1v P;vGqIh/񞩅z/w"e|km͘uP2ьb}: vvp1܃ ^nE/Ո@q̱ cy~G Oy,ǁ!WӯƇL7GyQ&5NMO} Nz8tڈΏR,8467e<6jqIX粍hL*rPEe/|>\K 0ވx!4 3i \\0^i7Aob%㌏Xx.(/hu=s~uD7(sc>j xU XizuBL%qIm/,LFHʥ Y= ! q @m y΢`Z-]d zfҝ蒼iHF"g|uO-!J%@]`<ǿ7u~B`$J¶b4MD8}FP\>*etn/:qf#^xچN*'wϞ|ruS3py^@p`'(efvPW\2Ap%p KyaEs47Ntt0Ӄ+b"w ZԭpppH.#(p)uN0lb94B\Ej$r"Hk7,Ox\I~=h.9^]cÃ5AKb'nbJ]OmT gT5/ Hcap'c3!! #q,`]d7ƒ jlt[0u9P4R/i 3?x_J|a6ciÎBiGi2 "dgpkd|gB?liKwsy3'oU,m~V ɒI>Dί`ds-ѕNtR ce2f)3"zE!Mw9D.|0#򐽺w+M>tndGj TrU,D Ђ OVGvk"WL6]޲iIZ_̑O:}U{/Dnʙw?>OE+*+l0(VE=[/(x޲q s(|tS-᧖{Yrb(+9OqF'U{]1wP^ڗد4>\AY!$FcB {/ ġ=:겦^ 66/FxvP$m'CX?\;٦w爊ש+>YYw]'XrW[l5kLTy Hf Jw_}@>>D*:`% Q{[wMa:γ,W6T@h3 UW pn2095C.3.QQeVQV`xτArHD#Y#`WX{CkA},Hx-[:Cm);bk~"'ma -\\9Y5)J*jUyAnH~_b`gd(2ǭqUʏ-``%?b%Q-c{DsU_Ŀ3)ݞ)pJ>r ~}5/xɊ/ Ns[;iD(֮Ϛ0oLWlU [Stn?. h0 3NLK7!08gI$.o)w I%5v3ܻgIX;lxlb{e+\< iS2 YzC*= ?y9^9A>`.zs qrceWEu݇߅lw_S$pBM5\J}~l H=u.HW.,_=X3=23_be8/g6}.~;GB|IN+*dl |kH%A̫vռ{'vl# % &ϑa!f<:*\!ܖ QMh c*p'b$2Ոm9w_T>ڀKB bLI!˦g .~RZ>ECt?ػ4ò!5!_mg1UIQ29tzSÅ_s9)-isH'b$qM&LuӬ(EFmFi4;;*Þ^g`c_oOU*,, ,v|02Gǵ5W\( r&Eiî%Tyѝ+&2mh7t3 @#".L(nowg3cg"7Ԍ0+b")ey2ո)tEA1n?Ub{|rҊ>AϣNH$1M&\O`~ DTrTȌw˹IJG> 4йAA&$nilhi5܋œ a_,lBeJlEبZLt@.CQtY HH^G8{;pG%G<= |&O#[`kٽd3$w~9pE\:o\ACzr|+{5cRkFp=DTϾF&*Tvˆrn}kBZ7RmΈOevn.{}~R<*]?OU% l_E&[9 nޯ>sfzf⠆fp ϷW{e]TeV0A(kڧnnAΐ:{<2Rgiada-0.22.0/scripts/000077500000000000000000000000001425106661500143355ustar00rootroot00000000000000giada-0.22.0/scripts/create_source_tarball.sh000077500000000000000000000061131425106661500212210ustar00rootroot00000000000000#!/usr/bin/env bash # # Creates source tarballs for giada in the form of # 'giada-x.x.x-src.tar.gz' and optionally detached PGP signatures # for the created file of the form 'giada-x.x.x-src.tar.gz.asc'. # If the environment variable BUILD_DIR is provided, the files will be moved to # $BUILD_DIR/, else to the location of this script (the repository folder). # # Requirements: # - git # - tar # - a writable (user) /tmp folder for mktemp # - gnupg >= 2.0.0 (if source tarball signing is requested) # - a valid PGP signing key in the keyring (if source tarball signing is # requested) set -euo pipefail get_absolute_path() { cd "$(dirname "$1")" && pwd -P } validate_project_tag() { if ! git ls-remote -t "${upstream}"| grep -e "${version}$" > /dev/null; then echo "The tag '$version' could not be found in upstream repository (${upstream})." exit 1 fi } checkout_project() { echo "Cloning project below working directory ${working_dir}" cd "$working_dir" git clone "$upstream" --branch "$version" \ --single-branch \ --depth=1 \ --recurse-submodules \ --shallow-submodules \ "${output_name}" } clean_sources() { cd "${working_dir}/${output_name}" echo "Removing unneeded files and folders..." rm -rfv .git* \ .travis* \ create_source_tarball.sh } compress_sources() { cd "${working_dir}" tar cvfz "${output_name}.tar.gz" "${output_name}" } move_sources() { cd "${working_dir}" mv -v "${output_name}.tar.gz" "${output_dir}/" } sign_sources() { cd "${output_dir}" gpg --detach-sign \ -u "${signer}" \ -o "${output_name}.tar.gz.asc" \ "${output_name}.tar.gz" } cleanup_working_dir() { echo "Removing working directory: ${working_dir}" rm -rf "${working_dir}" } print_help() { echo "Usage: $0 -v -s " exit 1 } if [ -n "${BUILD_DIR:-}" ]; then echo "Build dir provided: ${BUILD_DIR}" output_dir="${BUILD_DIR}/" mkdir -p "${output_dir}" else output_dir="$(get_absolute_path "$0")" fi upstream="https://github.com/monocasual/giada" package_name="giada" working_dir="$(mktemp -d)" version="$(date '+%Y-%m-%d')" output_version="" output_name="" signer="" signature=0 # remove the working directory, no matter what trap cleanup_working_dir EXIT if [ ${#@} -gt 0 ]; then while getopts 'hv:s:' flag; do case "${flag}" in h) print_help ;; s) signer=$OPTARG signature=1 ;; v) version=$OPTARG output_version="${version//v}" ;; *) echo "Error! Try '${0} -h'." exit 1 ;; esac done else print_help fi output_name="${package_name}-${output_version}-src" validate_project_tag checkout_project clean_sources compress_sources move_sources if [ $signature -eq 1 ]; then sign_sources fi exit 0 # vim:set ts=4 sw=4 et: giada-0.22.0/src/000077500000000000000000000000001425106661500134355ustar00rootroot00000000000000giada-0.22.0/src/core/000077500000000000000000000000001425106661500143655ustar00rootroot00000000000000giada-0.22.0/src/core/actions/000077500000000000000000000000001425106661500160255ustar00rootroot00000000000000giada-0.22.0/src/core/actions/action.h000066400000000000000000000033031425106661500174520ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_ACTION_H #define G_ACTION_H #include "src/core/midiEvent.h" #include "src/core/types.h" namespace giada::m { struct Action { ID id = 0; // Invalid ID channelId; Frame frame; MidiEvent event; ID pluginId = -1; int pluginParam = -1; ID prevId = 0; ID nextId = 0; const Action* prev = nullptr; const Action* next = nullptr; bool isValid() const { return id != 0; } bool isVolumeEnvelope() const { return event.getStatus() == MidiEvent::ENVELOPE && pluginId == -1; } }; } // namespace giada::m #endifgiada-0.22.0/src/core/actions/actionRecorder.cpp000066400000000000000000000231401425106661500214740ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/actions/actionRecorder.h" #include "core/actions/action.h" #include "core/actions/actions.h" #include "core/const.h" #include "core/model/model.h" #include "core/patch.h" #include "utils/log.h" #include "utils/ver.h" #include #include #include #include #include namespace giada::m { namespace { constexpr int MAX_LIVE_RECS_CHUNK = 128; } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ ActionRecorder::ActionRecorder(model::Model& m) : m_model(m) , m_actions(m) { m_liveActions.reserve(MAX_LIVE_RECS_CHUNK); } /* -------------------------------------------------------------------------- */ void ActionRecorder::reset() { m_liveActions.clear(); m_actions.reset(); } /* -------------------------------------------------------------------------- */ bool ActionRecorder::isBoundaryEnvelopeAction(const Action& a) const { assert(a.prev != nullptr); assert(a.next != nullptr); return a.prev->frame > a.frame || a.next->frame < a.frame; } /* -------------------------------------------------------------------------- */ void ActionRecorder::updateBpm(float ratio, int quantizerStep) { if (ratio == 1.0f) return; m_actions.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 ('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(old * ratio); if (frame != 0) { Frame delta = quantizerStep % frame; if (delta > 0 && delta <= 6) frame = frame + delta; } return frame; }); } /* -------------------------------------------------------------------------- */ void ActionRecorder::updateSamplerate(int systemRate, int patchRate) { if (systemRate == patchRate) return; float ratio = systemRate / (float)patchRate; m_actions.updateKeyFrames([=](Frame old) { return floorf(old * ratio); }); } /* -------------------------------------------------------------------------- */ bool ActionRecorder::cloneActions(ID channelId, ID newChannelId) { bool cloned = false; std::vector actions; std::unordered_map map; // Action ID mapper, old -> new m_actions.forEachAction([&](const Action& a) { if (a.channelId != channelId) return; ID newActionId = m_actions.getNewActionId(); map.insert({a.id, newActionId}); Action clone(a); clone.id = newActionId; clone.channelId = newChannelId; actions.push_back(clone); cloned = true; }); /* 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); } m_actions.rec(actions); return cloned; } /* -------------------------------------------------------------------------- */ void ActionRecorder::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 (m_liveActions.size() >= m_liveActions.capacity()) m_liveActions.reserve(m_liveActions.size() + MAX_LIVE_RECS_CHUNK); m_liveActions.push_back(m_actions.makeAction(m_actions.getNewActionId(), channelId, globalFrame, e)); } /* -------------------------------------------------------------------------- */ std::unordered_set ActionRecorder::consolidate() { for (auto it = m_liveActions.begin(); it != m_liveActions.end(); ++it) consolidate(*it, it - m_liveActions.begin()); // Pass current index m_actions.rec(m_liveActions); std::unordered_set out; for (const Action& action : m_liveActions) out.insert(action.channelId); m_liveActions.clear(); return out; } /* -------------------------------------------------------------------------- */ void ActionRecorder::clearAllActions() { for (Channel& ch : m_model.get().channels) ch.hasActions = false; m_model.swap(model::SwapType::HARD); m_actions.clearAll(); } /* -------------------------------------------------------------------------- */ Actions::Map ActionRecorder::deserializeActions(const std::vector& pactions) { Actions::Map out; /* First pass: add actions with no relationship, that is with no prev/next pointers filled in. */ for (const Patch::Action& paction : pactions) out[paction.frame].push_back(m_actions.makeAction(paction)); /* 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) continue; Action* curr = const_cast(getActionPtrById(paction.id, out)); assert(curr != nullptr); if (paction.nextId != 0) { curr->next = getActionPtrById(paction.nextId, out); assert(curr->next != nullptr); } if (paction.prevId != 0) { curr->prev = getActionPtrById(paction.prevId, out); assert(curr->prev != nullptr); } } return out; } /* -------------------------------------------------------------------------- */ std::vector ActionRecorder::serializeActions(const Actions::Map& actions) { std::vector out; 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, }); } } return out; } /* -------------------------------------------------------------------------- */ const Action* ActionRecorder::getActionPtrById(int id, const Actions::Map& source) { for (const auto& [_, actions] : source) for (const Action& action : actions) if (action.id == id) return &action; return nullptr; } /* -------------------------------------------------------------------------- */ bool ActionRecorder::areComposite(const Action& a1, const Action& a2) const { return a1.event.getStatus() == MidiEvent::NOTE_ON && a2.event.getStatus() == MidiEvent::NOTE_OFF && a1.event.getNote() == a2.event.getNote() && a1.channelId == a2.channelId; } /* -------------------------------------------------------------------------- */ void ActionRecorder::consolidate(const Action& a1, std::size_t i) { /* This algorithm must start searching from the element next to 'a1': since live actions are recorded in linear sequence, the potential partner of 'a1' always lies beyond a1 itself. Without this trick (i.e. if it loops from vector.begin() each time) the algorithm would end up matching wrong partners. */ for (auto it = m_liveActions.begin() + i; it != m_liveActions.end(); ++it) { const Action& a2 = *it; if (!areComposite(a1, a2)) continue; const_cast(a1).nextId = a2.id; const_cast(a2).prevId = a1.id; break; } } /* -------------------------------------------------------------------------- */ const std::vector* ActionRecorder::getActionsOnFrame(Frame f) const { return m_actions.getActionsOnFrame(f); } bool ActionRecorder::hasActions(ID channelId, int type) const { return m_actions.hasActions(channelId, type); } Action ActionRecorder::getClosestAction(ID channelId, Frame f, int type) const { return m_actions.getClosestAction(channelId, f, type); } std::vector ActionRecorder::getActionsOnChannel(ID channelId) const { return m_actions.getActionsOnChannel(channelId); } void ActionRecorder::clearChannel(ID channelId) { m_actions.clearChannel(channelId); } void ActionRecorder::clearActions(ID channelId, int type) { m_actions.clearActions(channelId, type); } Action ActionRecorder::rec(ID channelId, Frame frame, MidiEvent e) { return m_actions.rec(channelId, frame, e); } void ActionRecorder::rec(ID channelId, Frame f1, Frame f2, MidiEvent e1, MidiEvent e2) { return m_actions.rec(channelId, f1, f2, e1, e2); } void ActionRecorder::updateSiblings(ID id, ID prevId, ID nextId) { m_actions.updateSiblings(id, prevId, nextId); } void ActionRecorder::deleteAction(ID id) { m_actions.deleteAction(id); } void ActionRecorder::deleteAction(ID currId, ID nextId) { m_actions.deleteAction(currId, nextId); } void ActionRecorder::updateEvent(ID id, MidiEvent e) { m_actions.updateEvent(id, e); } } // namespace giada::m giada-0.22.0/src/core/actions/actionRecorder.h000066400000000000000000000101461425106661500211430ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_ACTION_RECORDER_H #define G_ACTION_RECORDER_H #include "core/actions/actions.h" #include "core/midiEvent.h" #include "core/types.h" #include #include namespace giada::m::patch { struct Action; } namespace giada::m { struct Action; class ActionRecorder { public: ActionRecorder(model::Model&); /* reset Brings everything back to the initial state. */ void reset(); bool isBoundaryEnvelopeAction(const Action& a) const; /* updateBpm Changes actions position by calculating the new bpm value. */ void updateBpm(float ratio, int quantizerStep); /* updateSamplerate Changes actions position by taking in account the new samplerate. If f_system == f_patch nothing will change, otherwise the conversion is mandatory. */ void updateSamplerate(int systemRate, int patchRate); /* cloneActions Clones actions in channel 'channelId', giving them a new channel ID. Returns whether any action has been cloned. */ bool cloneActions(ID channelId, ID newChannelId); /* liveRec Records a user-generated action. NOTE_ON or NOTE_OFF only for now. */ void liveRec(ID channelId, MidiEvent e, Frame global); /* consolidate Records all live actions. Returns a set of channels IDs that have been recorded. */ std::unordered_set consolidate(); /* clearAllActions Deletes all recorded actions. */ void clearAllActions(); /* (de)serializeActions Creates new Actions given the patch raw data and vice versa. */ Actions::Map deserializeActions(const std::vector& as); std::vector serializeActions(const Actions::Map& as); /* Pass-thru functions. See Actions.h */ const std::vector* getActionsOnFrame(Frame f) const; bool hasActions(ID channelId, int type = 0) const; Action getClosestAction(ID channelId, Frame f, int type) const; std::vector getActionsOnChannel(ID channelId) const; void clearChannel(ID channelId); void clearActions(ID channelId, int type); Action rec(ID channelId, Frame frame, MidiEvent e); void rec(ID channelId, Frame f1, Frame f2, MidiEvent e1, MidiEvent e2); void updateSiblings(ID id, ID prevId, ID nextId); void deleteAction(ID id); void deleteAction(ID currId, ID nextId); void updateEvent(ID id, MidiEvent e); private: /* areComposite Composite: NOTE_ON + NOTE_OFF on the same note. */ bool areComposite(const Action& a1, const Action& a2) const; const Action* getActionPtrById(int id, const Actions::Map& source); /* consolidate Given an action 'a1' tries to find the matching NOTE_OFF and updates the action accordingly. */ void consolidate(const Action& a1, std::size_t i); model::Model& m_model; Actions m_actions; std::vector m_liveActions; }; } // namespace giada::m #endif giada-0.22.0/src/core/actions/actions.cpp000066400000000000000000000225661425106661500202040ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "actions.h" #include "action.h" #include "core/idManager.h" #include "core/model/model.h" #include "utils/log.h" #include #include #include namespace giada::m { Actions::Actions(model::Model& model) : m_model(model) { } /* -------------------------------------------------------------------------- */ void Actions::reset() { m_actionId = IdManager(); clearAll(); } /* -------------------------------------------------------------------------- */ void Actions::clearAll() { model::DataLock lock = m_model.lockData(); m_model.getAllShared().clear(); } /* -------------------------------------------------------------------------- */ void Actions::clearChannel(ID channelId) { removeIf([=](const Action& a) { return a.channelId == channelId; }); } /* -------------------------------------------------------------------------- */ void Actions::clearActions(ID channelId, int type) { removeIf([=](const Action& a) { return a.channelId == channelId && a.event.getStatus() == type; }); } /* -------------------------------------------------------------------------- */ void Actions::deleteAction(ID id) { removeIf([=](const Action& a) { return a.id == id; }); } void Actions::deleteAction(ID currId, ID nextId) { removeIf([=](const Action& a) { return a.id == currId || a.id == nextId; }); } /* -------------------------------------------------------------------------- */ void Actions::updateKeyFrames(std::function f) { Map temp; /* 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] : m_model.getAllShared()) { 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(temp); model::DataLock lock = m_model.lockData(); m_model.getAllShared() = std::move(temp); } /* -------------------------------------------------------------------------- */ void Actions::updateEvent(ID id, MidiEvent e) { model::DataLock lock = m_model.lockData(); findAction(m_model.getAllShared(), id)->event = e; } /* -------------------------------------------------------------------------- */ void Actions::updateSiblings(ID id, ID prevId, ID nextId) { model::DataLock lock = m_model.lockData(); Action* pcurr = findAction(m_model.getAllShared(), id); Action* pprev = findAction(m_model.getAllShared(), prevId); Action* pnext = findAction(m_model.getAllShared(), 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; } } /* -------------------------------------------------------------------------- */ bool Actions::hasActions(ID channelId, int type) const { for (const auto& [frame, actions] : m_model.getAllShared()) for (const Action& a : actions) if (a.channelId == channelId && (type == 0 || type == a.event.getStatus())) return true; return false; } /* -------------------------------------------------------------------------- */ Action Actions::makeAction(ID id, ID channelId, Frame frame, MidiEvent e) { Action out{m_actionId.generate(id), channelId, frame, e, -1, -1}; m_actionId.set(id); return out; } Action Actions::makeAction(const Patch::Action& a) { m_actionId.set(a.id); return Action{a.id, a.channelId, a.frame, a.event, -1, -1, a.prevId, a.nextId}; } /* -------------------------------------------------------------------------- */ Action Actions::rec(ID channelId, Frame frame, MidiEvent event) { /* Skip duplicates. */ if (exists(channelId, frame, event)) 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::DataLock lock = m_model.lockData(); m_model.getAllShared()[frame].push_back(a); updateMapPointers(m_model.getAllShared()); return a; } /* -------------------------------------------------------------------------- */ void Actions::rec(std::vector& actions) { if (actions.size() == 0) return; model::DataLock lock = m_model.lockData(); Map& map = m_model.getAllShared(); for (const Action& a : actions) if (!exists(a.channelId, a.frame, a.event, map)) map[a.frame].push_back(a); updateMapPointers(map); } /* -------------------------------------------------------------------------- */ void Actions::rec(ID channelId, Frame f1, Frame f2, MidiEvent e1, MidiEvent e2) { model::DataLock lock = m_model.lockData(); Map& map = m_model.getAllShared(); 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* Actions::getActionsOnFrame(Frame frame) const { if (m_model.getAllShared().count(frame) == 0) return nullptr; return &m_model.getAllShared().at(frame); } /* -------------------------------------------------------------------------- */ Action Actions::getClosestAction(ID channelId, Frame f, int type) const { Action out = {}; forEachAction([&](const Action& a) { if (a.event.getStatus() != type || a.channelId != channelId) return; if (!out.isValid() || (a.frame <= f && a.frame > out.frame)) out = a; }); return out; } /* -------------------------------------------------------------------------- */ std::vector Actions::getActionsOnChannel(ID channelId) const { std::vector out; forEachAction([&](const Action& a) { if (a.channelId == channelId) out.push_back(a); }); return out; } /* -------------------------------------------------------------------------- */ void Actions::forEachAction(std::function f) const { for (auto& [_, actions] : m_model.getAllShared()) for (const Action& action : actions) f(action); } /* -------------------------------------------------------------------------- */ ID Actions::getNewActionId() { return m_actionId.generate(); } /* -------------------------------------------------------------------------- */ Action* Actions::findAction(Map& src, ID id) { for (auto& [frame, actions] : src) for (Action& a : actions) if (a.id == id) return &a; assert(false); return nullptr; } /* -------------------------------------------------------------------------- */ void Actions::updateMapPointers(Map& 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 Actions::optimize(Map& map) { for (auto it = map.cbegin(); it != map.cend();) it->second.size() == 0 ? it = map.erase(it) : ++it; } /* -------------------------------------------------------------------------- */ void Actions::removeIf(std::function f) { model::DataLock lock = m_model.lockData(); Map& map = m_model.getAllShared(); for (auto& [frame, actions] : map) actions.erase(std::remove_if(actions.begin(), actions.end(), f), actions.end()); optimize(map); updateMapPointers(map); } /* -------------------------------------------------------------------------- */ bool Actions::exists(ID channelId, Frame frame, const MidiEvent& event, const Map& target) const { for (const auto& [_, actions] : target) for (const Action& a : actions) if (a.channelId == channelId && a.frame == frame && a.event.getRaw() == event.getRaw()) return true; return false; } /* -------------------------------------------------------------------------- */ bool Actions::exists(ID channelId, Frame frame, const MidiEvent& event) const { return exists(channelId, frame, event, m_model.getAllShared()); } } // namespace giada::m giada-0.22.0/src/core/actions/actions.h000066400000000000000000000117321425106661500176420ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_ACTIONS_H #define G_ACTIONS_H #include "action.h" #include "core/idManager.h" #include "core/midiEvent.h" #include "core/patch.h" #include "core/types.h" #include #include #include #include namespace giada::m::model { class Model; } namespace giada::m { class Actions { public: using Map = std::map>; Actions(model::Model& model); /* forEachAction Applies a read-only callback on each action recorded. NEVER do anything inside the callback that might alter the ActionMap. */ void forEachAction(std::function f) const; /* getActionsOnChannel Returns a vector of actions belonging to channel 'ch'. */ std::vector getActionsOnChannel(ID channelId) const; /* getClosestAction Given a frame 'f' returns the closest action. */ Action getClosestAction(ID channelId, Frame f, int type) const; /* getActionsOnFrame Returns a pointer to a vector of actions recorded on frame 'f', or nullptr if the frame has no actions. */ const std::vector* getActionsOnFrame(Frame f) const; /* hasActions Checks if the channel has at least one action recorded. */ bool hasActions(ID channelId, int type = 0) const; /* makeAction Makes a new action given some data. */ //TODO - move to actionManager Action makeAction(ID id, ID channelId, Frame frame, MidiEvent e); Action makeAction(const Patch::Action& a); /* reset Brings everything back to the initial state. */ void reset(); /* clearAll Deletes all recorded actions. */ void clearAll(); /* clearChannel Clears all actions from a channel. */ void clearChannel(ID channelId); /* clearActions Clears the actions by type from a channel. */ void clearActions(ID channelId, int type); /* deleteAction (1) Deletes a specific action. */ void deleteAction(ID id); /* deleteAction (2) Deletes a specific pair of actions. Useful for composite stuff (i.e. MIDI). */ void deleteAction(ID currId, ID nextId); /* updateKeyFrames Update all the key frames in the internal map of actions, according to a lambda function 'f'. */ void updateKeyFrames(std::function f); /* updateEvent Changes the event in action 'a'. */ void updateEvent(ID id, MidiEvent e); /* updateSiblings Changes previous and next actions in action with id 'id'. Mostly used for chained actions such as envelopes. */ void updateSiblings(ID id, ID prevId, ID nextId); /* rec (1) Records an action and returns it. Used by the Action Editor. */ Action rec(ID channelId, Frame frame, MidiEvent e); /* rec (2) Transfer a vector of actions into the current ActionMap. This is called by recordHandler when a live session is over and consolidation is required. */ void rec(std::vector& actions); /* rec (3) Records two actions on channel 'channel'. Useful when recording composite actions in the Action Editor. */ void rec(ID channelId, Frame f1, Frame f2, MidiEvent e1, MidiEvent e2); /* getNewActionId Returns a new action ID, internally generated. */ //TODO - move to actionManager ID getNewActionId(); private: bool exists(ID channelId, Frame frame, const MidiEvent& event, const Map& target) const; bool exists(ID channelId, Frame frame, const MidiEvent& event) const; Action* findAction(Map& src, ID id); /* 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(Map& src); /* optimize Removes frames without actions. */ void optimize(Map& map); void removeIf(std::function f); model::Model& m_model; //TODO - move to actionManager IdManager m_actionId; }; } // namespace giada::m #endif giada-0.22.0/src/core/channels/000077500000000000000000000000001425106661500161605ustar00rootroot00000000000000giada-0.22.0/src/core/channels/audioReceiver.cpp000066400000000000000000000036601425106661500214570ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "audioReceiver.h" #include "core/channels/channel.h" #include "deps/mcl-audio-buffer/src/audioBuffer.hpp" namespace giada::m { AudioReceiver::AudioReceiver(const Patch::Channel& p) : inputMonitor(p.inputMonitor) , overdubProtection(p.overdubProtection) { } /* -------------------------------------------------------------------------- */ void AudioReceiver::render(const Channel& ch, const mcl::AudioBuffer& in) const { /* 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). */ if (ch.armed && inputMonitor) ch.shared->audioBuffer.set(in, /*gain=*/1.0f); // add, don't overwrite } } // namespace giada::m giada-0.22.0/src/core/channels/audioReceiver.h000066400000000000000000000031171425106661500211210ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_CHANNEL_AUDIO_RECEIVER_H #define G_CHANNEL_AUDIO_RECEIVER_H #include "core/patch.h" namespace mcl { class AudioBuffer; } namespace giada::m { class Channel; class AudioReceiver final { public: AudioReceiver() = default; AudioReceiver(const Patch::Channel& p); AudioReceiver(const AudioReceiver& o) = default; void render(const Channel& ch, const mcl::AudioBuffer& in) const; bool inputMonitor; bool overdubProtection; }; } // namespace giada::m #endif giada-0.22.0/src/core/channels/channel.cpp000066400000000000000000000274221425106661500203030ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/channels/channel.h" #include "core/actions/actionRecorder.h" #include "core/channels/sampleAdvancer.h" #include "core/conf.h" #include "core/engine.h" #include "core/midiMapper.h" #include "core/mixerHandler.h" #include "core/model/model.h" #include "core/plugins/pluginHost.h" #include "core/plugins/pluginManager.h" #include "core/recorder.h" #include extern giada::m::Engine g_engine; namespace giada::m { namespace { mcl::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}; } } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ Channel::Channel(ChannelType type, ID id, ID columnId, ChannelShared& s) : shared(&s) , id(id) , type(type) , columnId(columnId) , volume(G_DEFAULT_VOL) , volume_i(G_DEFAULT_VOL) , pan(G_DEFAULT_PAN) , armed(false) , key(0) , hasActions(false) , height(G_GUI_UNIT) , midiLighter(g_engine.midiMapper) , m_mute(false) , m_solo(false) { switch (type) { case ChannelType::SAMPLE: samplePlayer.emplace(&(shared->resampler.value())); sampleAdvancer.emplace(); sampleReactor.emplace(*this, id); audioReceiver.emplace(); sampleActionRecorder.emplace(g_engine.actionRecorder, g_engine.sequencer); break; case ChannelType::PREVIEW: samplePlayer.emplace(&(shared->resampler.value())); sampleReactor.emplace(*this, id); break; case ChannelType::MIDI: midiController.emplace(); midiSender.emplace(g_engine.kernelMidi); midiActionRecorder.emplace(g_engine.actionRecorder, g_engine.sequencer); #ifdef WITH_VST midiReceiver.emplace(); #endif break; default: break; } initCallbacks(); } /* -------------------------------------------------------------------------- */ Channel::Channel(const Patch::Channel& p, ChannelShared& s, float samplerateRatio, Wave* wave) : shared(&s) , id(p.id) , type(p.type) , columnId(p.columnId) , volume(p.volume) , volume_i(G_DEFAULT_VOL) , pan(p.pan) , armed(p.armed) , key(p.key) , hasActions(p.hasActions) , name(p.name) , height(p.height) #ifdef WITH_VST , plugins(g_engine.pluginManager.hydratePlugins(p.pluginIds, g_engine.model)) // TODO move outside, as constructor parameter #endif , midiLearner(p) , midiLighter(g_engine.midiMapper, p) , m_mute(p.mute) , m_solo(p.solo) { shared->readActions.store(p.readActions); shared->recStatus.store(p.readActions ? ChannelStatus::PLAY : ChannelStatus::OFF); switch (type) { case ChannelType::SAMPLE: samplePlayer.emplace(p, samplerateRatio, &(shared->resampler.value()), wave); sampleAdvancer.emplace(); sampleReactor.emplace(*this, id); audioReceiver.emplace(p); sampleActionRecorder.emplace(g_engine.actionRecorder, g_engine.sequencer); break; case ChannelType::PREVIEW: samplePlayer.emplace(p, samplerateRatio, &(shared->resampler.value()), nullptr); sampleReactor.emplace(*this, id); break; case ChannelType::MIDI: midiController.emplace(); midiSender.emplace(p, g_engine.kernelMidi); midiActionRecorder.emplace(g_engine.actionRecorder, g_engine.sequencer); #ifdef WITH_VST midiReceiver.emplace(); #endif break; default: break; } initCallbacks(); } /* -------------------------------------------------------------------------- */ Channel::Channel(const Channel& other) : midiLighter(g_engine.midiMapper) { *this = other; } /* -------------------------------------------------------------------------- */ Channel& Channel::operator=(const Channel& other) { if (this == &other) return *this; shared = other.shared; id = other.id; type = other.type; columnId = other.columnId; volume = other.volume; volume_i = other.volume_i; pan = other.pan; m_mute = other.m_mute; m_solo = other.m_solo; armed = other.armed; key = other.key; hasActions = other.hasActions; name = other.name; height = other.height; #ifdef WITH_VST plugins = other.plugins; #endif midiLearner = other.midiLearner; midiLighter = other.midiLighter; samplePlayer = other.samplePlayer; sampleAdvancer = other.sampleAdvancer; sampleReactor = other.sampleReactor; audioReceiver = other.audioReceiver; midiController = other.midiController; #ifdef WITH_VST midiReceiver = other.midiReceiver; #endif midiSender = other.midiSender; sampleActionRecorder = other.sampleActionRecorder; midiActionRecorder = other.midiActionRecorder; initCallbacks(); return *this; } /* -------------------------------------------------------------------------- */ bool Channel::operator==(const Channel& other) { return id == other.id; } /* -------------------------------------------------------------------------- */ bool Channel::isInternal() const { return type == ChannelType::MASTER || type == ChannelType::PREVIEW; } bool Channel::isMuted() const { /* Internals can't be muted. */ return !isInternal() && m_mute; } bool Channel::isSoloed() const { return m_solo; } bool Channel::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::canActionRec() const { return hasWave() && !samplePlayer->isAnyLoopMode(); } bool Channel::hasWave() const { return samplePlayer && samplePlayer->hasWave(); } bool Channel::isPlaying() const { ChannelStatus s = shared->playStatus.load(); return s == ChannelStatus::PLAY || s == ChannelStatus::ENDING; } bool Channel::isReadingActions() const { ChannelStatus s = shared->recStatus.load(); return s == ChannelStatus::PLAY || s == ChannelStatus::ENDING; } /* -------------------------------------------------------------------------- */ void Channel::setMute(bool v) { if (m_mute != v) midiLighter.sendMute(v); m_mute = v; } void Channel::setSolo(bool v) { if (m_solo != v) midiLighter.sendSolo(v); m_solo = v; } /* -------------------------------------------------------------------------- */ void Channel::initCallbacks() { shared->playStatus.onChange = [this](ChannelStatus status) { midiLighter.sendStatus(status, g_engine.mixer.isChannelAudible(*this)); }; if (samplePlayer) { samplePlayer->onLastFrame = [this](bool natural) { sampleAdvancer->onLastFrame(*this, g_engine.sequencer.isRunning(), natural); }; } } /* -------------------------------------------------------------------------- */ void Channel::advance(const Sequencer::EventBuffer& events, Range block, Frame quantizerStep) const { if (shared->quantizer) shared->quantizer->advance(block, quantizerStep); for (const Sequencer::Event& e : events) { if (midiController) midiController->advance(shared->playStatus, e); if (samplePlayer) sampleAdvancer->advance(*this, e); if (midiSender && isPlaying() && !isMuted()) midiSender->advance(id, e); #ifdef WITH_VST if (midiReceiver && isPlaying()) midiReceiver->advance(id, shared->midiQueue, e); #endif } } /* -------------------------------------------------------------------------- */ void Channel::react(const EventDispatcher::EventBuffer& events) { for (const EventDispatcher::Event& e : events) { if (e.channelId > 0 && e.channelId != id) continue; react(e); if (midiController) midiController->react(shared->playStatus, e); if (midiSender && isPlaying() && !isMuted()) midiSender->react(e); if (samplePlayer) samplePlayer->react(e); if (midiActionRecorder) midiActionRecorder->react(*this, e, g_engine.recorder.canRecordActions()); if (sampleActionRecorder) sampleActionRecorder->react(*this, e, g_engine.conf.data.treatRecsAsLoops, g_engine.sequencer.isRunning(), g_engine.recorder.canRecordActions()); if (sampleReactor) sampleReactor->react(*this, e, g_engine.conf.data.chansStopOnSeqHalt, g_engine.sequencer.canQuantize()); #ifdef WITH_VST if (midiReceiver) midiReceiver->react(shared->midiQueue, e); #endif } } /* -------------------------------------------------------------------------- */ void Channel::react(const EventDispatcher::Event& e) { switch (e.type) { case EventDispatcher::EventType::CHANNEL_VOLUME: volume = std::get(e.data); break; case EventDispatcher::EventType::CHANNEL_PAN: pan = std::get(e.data); break; case EventDispatcher::EventType::CHANNEL_MUTE: setMute(!isMuted()); break; case EventDispatcher::EventType::CHANNEL_TOGGLE_ARM: armed = !armed; break; case EventDispatcher::EventType::CHANNEL_SOLO: setSolo(!isSoloed()); g_engine.mixerHandler.updateSoloCount(); break; default: break; } } /* -------------------------------------------------------------------------- */ void Channel::render(mcl::AudioBuffer* out, mcl::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); } /* -------------------------------------------------------------------------- */ void Channel::renderMasterOut(mcl::AudioBuffer& out) const { shared->audioBuffer.set(out, /*gain=*/1.0f); #ifdef WITH_VST if (plugins.size() > 0) g_engine.pluginHost.processStack(shared->audioBuffer, plugins, nullptr); #endif out.set(shared->audioBuffer, volume); } /* -------------------------------------------------------------------------- */ void Channel::renderMasterIn(mcl::AudioBuffer& in) const { #ifdef WITH_VST if (plugins.size() > 0) g_engine.pluginHost.processStack(in, plugins, nullptr); #else (void)in; #endif } /* -------------------------------------------------------------------------- */ void Channel::renderChannel(mcl::AudioBuffer& out, mcl::AudioBuffer& in, bool audible) const { shared->audioBuffer.clear(); if (samplePlayer && isPlaying()) { SamplePlayer::Render render; while (shared->renderQueue->pop(render)) ; samplePlayer->render(*shared, render); } if (audioReceiver) audioReceiver->render(*this, 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 (midiReceiver) midiReceiver->render(*shared, plugins, g_engine.pluginHost); else if (plugins.size() > 0) g_engine.pluginHost.processStack(shared->audioBuffer, plugins, nullptr); #endif if (audible) out.sum(shared->audioBuffer, volume * volume_i, calcPanning_(pan)); } } // namespace giada::mgiada-0.22.0/src/core/channels/channel.h000066400000000000000000000106071425106661500177450ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_CHANNEL_H #define G_CHANNEL_H #include #ifdef WITH_VST #include "deps/juce-config.h" #endif #include "core/channels/audioReceiver.h" #include "core/channels/channelShared.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/sampleAdvancer.h" #include "core/channels/samplePlayer.h" #include "core/channels/sampleReactor.h" #include "core/const.h" #include "core/eventDispatcher.h" #include "core/midiEvent.h" #include "core/mixer.h" #include "core/patch.h" #include "core/queue.h" #include "core/resampler.h" #include "core/sequencer.h" #include "deps/mcl-audio-buffer/src/audioBuffer.hpp" #ifdef WITH_VST #include "core/channels/midiReceiver.h" #endif namespace giada::m { class Plugin; class Channel final { public: Channel(ChannelType t, ID id, ID columnId, ChannelShared&); Channel(const Patch::Channel& p, ChannelShared&, float samplerateRatio, Wave* w); Channel(const Channel& o); Channel(Channel&& o) = default; Channel& operator=(const Channel&); Channel& operator=(Channel&&) = default; bool operator==(const Channel&); /* advance Advances internal state by processing static events (e.g. pre-recorded actions or sequencer events) in the current block. */ void advance(const Sequencer::EventBuffer&, Range, Frame quantizerStep) const; /* render Renders audio data to I/O buffers. */ void render(mcl::AudioBuffer* out, mcl::AudioBuffer* in, bool audible) const; /* react Reacts to live events coming from the EventDispatcher (human events) and updates itself accordingly. */ void react(const EventDispatcher::EventBuffer& e); bool isPlaying() const; bool isReadingActions() const; bool isInternal() const; bool isMuted() const; bool isSoloed() const; bool canInputRec() const; bool canActionRec() const; bool hasWave() const; void setMute(bool); void setSolo(bool); ChannelShared* shared; 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 armed; int key; bool hasActions; std::string name; Pixel height; #ifdef WITH_VST std::vector plugins; #endif MidiLearner midiLearner; MidiLighter midiLighter; std::optional samplePlayer; std::optional sampleAdvancer; std::optional sampleReactor; std::optional audioReceiver; std::optional midiController; #ifdef WITH_VST std::optional midiReceiver; #endif std::optional midiSender; std::optional sampleActionRecorder; std::optional midiActionRecorder; private: void renderMasterOut(mcl::AudioBuffer&) const; void renderMasterIn(mcl::AudioBuffer&) const; void renderChannel(mcl::AudioBuffer& out, mcl::AudioBuffer& in, bool audible) const; void initCallbacks(); void react(const EventDispatcher::Event&); bool m_mute; bool m_solo; }; } // namespace giada::m #endif giada-0.22.0/src/core/channels/channelManager.cpp000066400000000000000000000134271425106661500215760ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/channels/channelManager.h" #include "core/channels/channel.h" #include "core/channels/samplePlayer.h" #include "core/conf.h" #include "core/const.h" #include "core/model/model.h" #include "core/patch.h" #include "core/plugins/plugin.h" #include "core/plugins/pluginHost.h" #include "core/wave.h" #include "glue/channel.h" #include #include namespace giada::m { ChannelManager::ChannelManager(const Conf::Data& c, model::Model& m) : m_conf(c) , m_model(m) { } /* -------------------------------------------------------------------------- */ ID ChannelManager::getNextId() const { return m_channelId.getNext(); } /* -------------------------------------------------------------------------- */ void ChannelManager::reset() { m_channelId = IdManager(); } /* -------------------------------------------------------------------------- */ Channel ChannelManager::create(ID channelId, ChannelType type, ID columnId, int bufferSize) { Channel out = Channel(type, m_channelId.generate(channelId), columnId, makeShared(type, bufferSize)); if (out.audioReceiver) out.audioReceiver->overdubProtection = m_conf.overdubProtectionDefaultOn; c::channel::setCallbacks(out); // UI callbacks return out; } /* -------------------------------------------------------------------------- */ Channel ChannelManager::create(const Channel& o, int bufferSize) { Channel out = Channel(o); out.id = m_channelId.generate(); out.shared = &makeShared(o.type, bufferSize); c::channel::setCallbacks(out); // UI callbacks return out; } /* -------------------------------------------------------------------------- */ Channel ChannelManager::deserializeChannel(const Patch::Channel& pch, float samplerateRatio, int bufferSize) { m_channelId.set(pch.id); Channel out = Channel(pch, makeShared(pch.type, bufferSize), samplerateRatio, m_model.findShared(pch.waveId)); c::channel::setCallbacks(out); // UI callbacks return out; } /* -------------------------------------------------------------------------- */ const Patch::Channel ChannelManager::serializeChannel(const Channel& c) { Patch::Channel pc; #ifdef WITH_VST for (const Plugin* p : c.plugins) pc.pluginIds.push_back(p->id); #endif pc.id = c.id; pc.type = c.type; pc.columnId = c.columnId; pc.height = c.height; pc.name = c.name; pc.key = c.key; pc.mute = c.isMuted(); pc.solo = c.isSoloed(); pc.volume = c.volume; pc.pan = c.pan; pc.hasActions = c.hasActions; pc.readActions = c.shared->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->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.type == ChannelType::MIDI) { pc.midiOut = c.midiSender->enabled; pc.midiOutChan = c.midiSender->filter; } return pc; } /* -------------------------------------------------------------------------- */ ChannelShared& ChannelManager::makeShared(ChannelType type, int bufferSize) { std::unique_ptr shared = std::make_unique(bufferSize); if (type == ChannelType::SAMPLE || type == ChannelType::PREVIEW) { shared->quantizer.emplace(); shared->renderQueue.emplace(); shared->resampler.emplace(static_cast(m_conf.rsmpQuality), G_MAX_IO_CHANS); } m_model.addShared(std::move(shared)); return m_model.backShared(); } } // namespace giada::m giada-0.22.0/src/core/channels/channelManager.h000066400000000000000000000046371425106661500212460ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_CHANNEL_MANAGER_H #define G_CHANNEL_MANAGER_H #include "core/channels/channel.h" #include "core/conf.h" #include "core/idManager.h" #include "core/patch.h" #include "core/types.h" namespace giada::m::model { class Model; } namespace giada::m { class KernelAudio; class ChannelManager final { public: ChannelManager(const Conf::Data&, model::Model&); /* getNextId Returns the next channel ID that will be assigned to a new channel. */ ID getNextId() const; /* reset Resets internal ID generator. */ void reset(); /* create (1) Creates a new channel. If channelId == 0 generates a new ID, reuse the one passed in otherwise. */ Channel create(ID channelId, ChannelType type, ID columnId, int bufferSize); /* create (2) Creates a new channel given an existing one (i.e. clone). */ Channel create(const Channel& ch, int bufferSize); /* (de)serializeWave Creates a new channel given the patch raw data and vice versa. */ Channel deserializeChannel(const Patch::Channel& c, float samplerateRatio, int bufferSize); const Patch::Channel serializeChannel(const Channel& c); private: ChannelShared& makeShared(ChannelType type, int bufferSize); IdManager m_channelId; const Conf::Data& m_conf; model::Model& m_model; }; } // namespace giada::m #endif giada-0.22.0/src/core/channels/channelShared.cpp000066400000000000000000000024501425106661500214240ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/channels/channelShared.h" namespace giada::m { ChannelShared::ChannelShared(Frame bufferSize) : audioBuffer(bufferSize, G_MAX_IO_CHANS) { } } // namespace giada::mgiada-0.22.0/src/core/channels/channelShared.h000066400000000000000000000050341425106661500210720ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_CHANNELSHARED_H #define G_CHANNELSHARED_H #include #ifdef WITH_VST #include "deps/juce-config.h" #endif #include "core/channels/samplePlayer.h" #include "core/const.h" #include "core/midiEvent.h" #include "core/queue.h" #include "core/resampler.h" #include "deps/mcl-audio-buffer/src/audioBuffer.hpp" namespace giada::m { struct ChannelShared final { using MidiQueue = Queue; using RenderQueue = Queue; ChannelShared(Frame bufferSize); mcl::AudioBuffer audioBuffer; #ifdef WITH_VST juce::MidiBuffer midiBuffer; MidiQueue midiQueue; #endif WeakAtomic tracker = 0; WeakAtomic playStatus = ChannelStatus::OFF; WeakAtomic recStatus = ChannelStatus::OFF; WeakAtomic readActions = false; std::optional quantizer; /* Optional render queue for sample-based channels. Used by SampleReactor and SampleAdvancer to instruct SamplePlayer how to render audio. */ std::optional renderQueue = {}; /* 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 = {}; }; } // namespace giada::m #endif giada-0.22.0/src/core/channels/midiActionRecorder.cpp000066400000000000000000000037101425106661500224330ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/channels/midiActionRecorder.h" #include "core/channels/channel.h" #include "core/conf.h" #include "core/eventDispatcher.h" #include "core/sequencer.h" #include "src/core/actions/action.h" #include "src/core/actions/actionRecorder.h" namespace giada::m { MidiActionRecorder::MidiActionRecorder(ActionRecorder& a, Sequencer& s) : m_actionRecorder(&a) , m_sequencer(&s) { } /* -------------------------------------------------------------------------- */ void MidiActionRecorder::react(Channel& ch, const EventDispatcher::Event& e, bool canRecordActions) { if (e.type == EventDispatcher::EventType::MIDI && canRecordActions) { MidiEvent flat(std::get(e.data).event); flat.setChannel(0); m_actionRecorder->liveRec(ch.id, flat, m_sequencer->getCurrentFrameQuantized()); ch.hasActions = true; } } } // namespace giada::mgiada-0.22.0/src/core/channels/midiActionRecorder.h000066400000000000000000000031161425106661500221000ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_CHANNEL_MIDI_ACTION_RECORDER_H #define G_CHANNEL_MIDI_ACTION_RECORDER_H #include "core/eventDispatcher.h" namespace giada::m { class ActionRecorder; class Sequencer; class Channel; class MidiActionRecorder final { public: MidiActionRecorder(ActionRecorder&, Sequencer&); void react(Channel&, const EventDispatcher::Event&, bool canRecordActions); private: ActionRecorder* m_actionRecorder; Sequencer* m_sequencer; }; } // namespace giada::m #endif giada-0.22.0/src/core/channels/midiController.cpp000066400000000000000000000055001425106661500216520ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "midiController.h" namespace giada::m { void MidiController::react(WeakAtomic& a_playStatus, const EventDispatcher::Event& e) const { const ChannelStatus playStatus = a_playStatus.load(); switch (e.type) { case EventDispatcher::EventType::KEY_PRESS: a_playStatus.store(press(playStatus)); break; case EventDispatcher::EventType::KEY_KILL: case EventDispatcher::EventType::SEQUENCER_STOP: a_playStatus.store(ChannelStatus::OFF); break; case EventDispatcher::EventType::SEQUENCER_REWIND: a_playStatus.store(onFirstBeat(playStatus)); default: break; } } /* -------------------------------------------------------------------------- */ void MidiController::advance(WeakAtomic& a_playStatus, const Sequencer::Event& e) const { if (e.type != Sequencer::EventType::FIRST_BEAT) return; a_playStatus.store(onFirstBeat(a_playStatus.load())); } /* -------------------------------------------------------------------------- */ ChannelStatus MidiController::onFirstBeat(ChannelStatus playStatus) const { if (playStatus == ChannelStatus::ENDING) playStatus = ChannelStatus::OFF; else if (playStatus == ChannelStatus::WAIT) playStatus = ChannelStatus::PLAY; return playStatus; } /* -------------------------------------------------------------------------- */ ChannelStatus MidiController::press(ChannelStatus playStatus) const { switch (playStatus) { case ChannelStatus::PLAY: playStatus = ChannelStatus::ENDING; break; case ChannelStatus::ENDING: case ChannelStatus::WAIT: playStatus = ChannelStatus::OFF; break; case ChannelStatus::OFF: playStatus = ChannelStatus::WAIT; break; default: break; } return playStatus; } } // namespace giada::m giada-0.22.0/src/core/channels/midiController.h000066400000000000000000000031341425106661500213200ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_CHANNEL_MIDI_CONTROLLER_H #define G_CHANNEL_MIDI_CONTROLLER_H #include "core/eventDispatcher.h" #include "core/sequencer.h" namespace giada::m { class MidiController final { public: void advance(WeakAtomic&, const Sequencer::Event& e) const; void react(WeakAtomic&, const EventDispatcher::Event& e) const; private: ChannelStatus onFirstBeat(ChannelStatus) const; ChannelStatus press(ChannelStatus) const; }; } // namespace giada::m #endif giada-0.22.0/src/core/channels/midiLearner.cpp000066400000000000000000000035271425106661500211260ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "midiLearner.h" #include "core/patch.h" namespace giada::m { MidiLearner::MidiLearner() : enabled(false) , filter(-1) { } /* -------------------------------------------------------------------------- */ MidiLearner::MidiLearner(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) { } /* -------------------------------------------------------------------------- */ bool MidiLearner::isAllowed(int c) const { return enabled && (filter == -1 || filter == c); } } // namespace giada::mgiada-0.22.0/src/core/channels/midiLearner.h000066400000000000000000000040711425106661500205660ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_CHANNEL_MIDI_LEARNER_H #define G_CHANNEL_MIDI_LEARNER_H #include "core/midiLearnParam.h" #include "core/patch.h" namespace giada::m { class MidiLearner final { public: MidiLearner(); MidiLearner(const Patch::Channel&); MidiLearner(const MidiLearner&) = default; /* isAllowed Tells whether the MIDI channel 'c' is enabled to receive MIDI data. */ bool isAllowed(int c) const; /* 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 #endif giada-0.22.0/src/core/channels/midiLighter.cpp000066400000000000000000000071041425106661500211270ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/channels/midiLighter.h" #include "core/kernelMidi.h" #include "core/midiMapper.h" namespace giada::m { template MidiLighter::MidiLighter(MidiMapper& m) : enabled(false) , onSend(nullptr) , m_midiMapper(&m) { } /* -------------------------------------------------------------------------- */ template MidiLighter::MidiLighter(MidiMapper& m, const Patch::Channel& p) : enabled(p.midiOutL) , playing(p.midiOutLplaying) , mute(p.midiOutLmute) , solo(p.midiOutLsolo) , m_midiMapper(&m) { } /* -------------------------------------------------------------------------- */ template void MidiLighter::sendStatus(ChannelStatus status, bool audible) { const MidiMap& midiMap = m_midiMapper->currentMap; const uint32_t l_playing = playing.getValue(); if (l_playing == 0x0) return; switch (status) { case ChannelStatus::OFF: send(l_playing, midiMap.stopped); break; case ChannelStatus::WAIT: send(l_playing, midiMap.waiting); break; case ChannelStatus::ENDING: send(l_playing, midiMap.stopping); break; case ChannelStatus::PLAY: send(l_playing, audible ? midiMap.playing : midiMap.playingInaudible); break; default: break; } } /* -------------------------------------------------------------------------- */ template void MidiLighter::sendMute(bool isMuted) { const MidiMap& midiMap = m_midiMapper->currentMap; const uint32_t l_mute = mute.getValue(); if (l_mute != 0x0) send(l_mute, isMuted ? midiMap.muteOn : midiMap.muteOff); } /* -------------------------------------------------------------------------- */ template void MidiLighter::sendSolo(bool isSoloed) { const MidiMap& midiMap = m_midiMapper->currentMap; const uint32_t l_solo = solo.getValue(); if (l_solo != 0x0) send(l_solo, isSoloed ? midiMap.soloOn : midiMap.soloOff); } /* -------------------------------------------------------------------------- */ template void MidiLighter::send(uint32_t learnt, const MidiMap::Message& msg) { assert(onSend != nullptr); m_midiMapper->sendMidiLightning(learnt, msg); onSend(); } /* -------------------------------------------------------------------------- */ template class MidiLighter; #ifdef WITH_TESTS template class MidiLighter; #endif } // namespace giada::mgiada-0.22.0/src/core/channels/midiLighter.h000066400000000000000000000042741425106661500206010ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_CHANNEL_MIDI_LIGHTER_H #define G_CHANNEL_MIDI_LIGHTER_H #include "core/eventDispatcher.h" #include "core/midiLearnParam.h" #include "core/midiMapper.h" #include "core/patch.h" namespace giada::m { template class MidiLighter final { public: MidiLighter(MidiMapper&); MidiLighter(MidiMapper&, const Patch::Channel&); MidiLighter(const MidiLighter& o) = default; void sendStatus(ChannelStatus, bool audible); void sendMute(bool isMuted); void sendSolo(bool isSoloed); /* enabled Tells whether MIDI lighting is enabled or not. */ bool enabled; /* MIDI learning fields for MIDI lighting. */ MidiLearnParam playing; MidiLearnParam mute; MidiLearnParam solo; /* onSend Callback fired when a MIDI signal has been sent. */ std::function onSend; private: void send(uint32_t learnt, const MidiMap::Message&); MidiMapper* m_midiMapper; }; extern template class MidiLighter; #ifdef WITH_TESTS extern template class MidiLighter; #endif } // namespace giada::m #endif giada-0.22.0/src/core/channels/midiReceiver.cpp000066400000000000000000000065611425106661500213030ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #include "midiReceiver.h" #include "core/eventDispatcher.h" #include "core/plugins/pluginHost.h" namespace giada::m { void MidiReceiver::react(ChannelShared::MidiQueue& midiQueue, const EventDispatcher::Event& e) const { switch (e.type) { case EventDispatcher::EventType::MIDI: parseMidi(midiQueue, std::get(e.data).event); break; case EventDispatcher::EventType::KEY_KILL: case EventDispatcher::EventType::SEQUENCER_STOP: case EventDispatcher::EventType::SEQUENCER_REWIND: sendToPlugins(midiQueue, MidiEvent(G_MIDI_ALL_NOTES_OFF), 0); break; default: break; } } /* -------------------------------------------------------------------------- */ void MidiReceiver::advance(ID channelId, ChannelShared::MidiQueue& midiQueue, const Sequencer::Event& e) const { if (e.type != Sequencer::EventType::ACTIONS) return; for (const Action& action : *e.actions) if (action.channelId == channelId) sendToPlugins(midiQueue, action.event, e.delta); } /* -------------------------------------------------------------------------- */ void MidiReceiver::render(ChannelShared& shared, const std::vector& plugins, PluginHost& pluginHost) const { shared.midiBuffer.clear(); MidiEvent e; while (shared.midiQueue.pop(e)) { juce::MidiMessage message = juce::MidiMessage( e.getStatus(), e.getNote(), e.getVelocity()); shared.midiBuffer.addEvent(message, e.getDelta()); } pluginHost.processStack(shared.audioBuffer, plugins, &shared.midiBuffer); } /* -------------------------------------------------------------------------- */ void MidiReceiver::sendToPlugins(ChannelShared::MidiQueue& midiQueue, const MidiEvent& e, Frame localFrame) const { midiQueue.push(MidiEvent(e.getRaw(), localFrame)); } /* -------------------------------------------------------------------------- */ void MidiReceiver::parseMidi(ChannelShared::MidiQueue& midiQueue, const MidiEvent& e) const { /* 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(midiQueue, flat, /*delta=*/0); } } // namespace giada::m #endif // WITH_VST giada-0.22.0/src/core/channels/midiReceiver.h000066400000000000000000000034721425106661500207460ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_CHANNEL_MIDI_RECEIVER_H #define G_CHANNEL_MIDI_RECEIVER_H #ifdef WITH_VST #include "core/channels/channelShared.h" #include "core/sequencer.h" namespace giada::m { class PluginHost; class Plugin; class MidiReceiver final { public: void react(ChannelShared::MidiQueue&, const EventDispatcher::Event&) const; void advance(ID channelId, ChannelShared::MidiQueue&, const Sequencer::Event&) const; void render(ChannelShared&, const std::vector&, PluginHost&) const; private: void sendToPlugins(ChannelShared::MidiQueue&, const MidiEvent&, Frame localFrame) const; void parseMidi(ChannelShared::MidiQueue&, const MidiEvent&) const; }; } // namespace giada::m #endif // WITH_VST #endif giada-0.22.0/src/core/channels/midiSender.cpp000066400000000000000000000051401425106661500207470ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/channels/midiSender.h" #include "core/kernelMidi.h" #include "core/mixer.h" namespace giada::m { MidiSender::MidiSender(KernelMidi& k) : kernelMidi(&k) , enabled(false) , filter(0) , onSend(nullptr) { } /* -------------------------------------------------------------------------- */ MidiSender::MidiSender(const Patch::Channel& p, KernelMidi& k) : kernelMidi(&k) , enabled(p.midiOut) , filter(p.midiOutChan) { } /* -------------------------------------------------------------------------- */ void MidiSender::react(const EventDispatcher::Event& e) { if (!enabled) return; if (e.type == EventDispatcher::EventType::KEY_KILL || e.type == EventDispatcher::EventType::SEQUENCER_STOP) send(MidiEvent(G_MIDI_ALL_NOTES_OFF)); } /* -------------------------------------------------------------------------- */ void MidiSender::advance(ID channelId, const Sequencer::Event& e) const { if (!enabled) return; if (e.type == Sequencer::EventType::ACTIONS) parseActions(channelId, *e.actions); } /* -------------------------------------------------------------------------- */ void MidiSender::send(MidiEvent e) const { assert(onSend != nullptr); e.setChannel(filter); kernelMidi->send(e.getRaw()); onSend(); } /* -------------------------------------------------------------------------- */ void MidiSender::parseActions(ID channelId, const std::vector& as) const { for (const Action& a : as) if (a.channelId == channelId) send(a.event); } } // namespace giada::mgiada-0.22.0/src/core/channels/midiSender.h000066400000000000000000000037041425106661500204200ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_CHANNEL_MIDI_SENDER_H #define G_CHANNEL_MIDI_SENDER_H #include "core/patch.h" #include "core/sequencer.h" namespace giada::m { class KernelMidi; class MidiSender final { public: MidiSender(KernelMidi&); MidiSender(const Patch::Channel& p, KernelMidi&); MidiSender(const MidiSender& o) = default; void react(const EventDispatcher::Event& e); void advance(ID channelId, const Sequencer::Event& e) const; KernelMidi* kernelMidi; /* enabled Tells whether MIDI output is enabled or not. */ bool enabled; /* filter Which MIDI channel data should be sent to. */ int filter; /* onSend Callback fired when a MIDI signal has been sent. */ std::function onSend; private: void send(MidiEvent e) const; void parseActions(ID channelId, const std::vector& as) const; }; } // namespace giada::m #endif giada-0.22.0/src/core/channels/sampleActionRecorder.cpp000066400000000000000000000134371425106661500230010ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/channels/sampleActionRecorder.h" #include "core/channels/channel.h" #include "core/eventDispatcher.h" #include "src/core/actions/action.h" #include "src/core/actions/actionRecorder.h" #include namespace giada::m { SampleActionRecorder::SampleActionRecorder(ActionRecorder& a, Sequencer& s) : m_actionRecorder(&a) , m_sequencer(&s) { } /* -------------------------------------------------------------------------- */ void SampleActionRecorder::react(Channel& ch, const EventDispatcher::Event& e, bool treatRecsAsLoops, bool seqIsRunning, bool canRecordActions) const { if (!ch.hasWave()) return; canRecordActions = canRecordActions && !ch.samplePlayer->isAnyLoopMode(); switch (e.type) { case EventDispatcher::EventType::KEY_PRESS: if (canRecordActions) onKeyPress(ch); break; case EventDispatcher::EventType::KEY_RELEASE: /* Record a stop event only if channel is SINGLE_PRESS. For any other mode the key release event is meaningless. */ if (canRecordActions && ch.samplePlayer->mode == SamplePlayerMode::SINGLE_PRESS) record(ch, MidiEvent::NOTE_OFF); break; case EventDispatcher::EventType::KEY_KILL: if (canRecordActions) record(ch, MidiEvent::NOTE_KILL); break; case EventDispatcher::EventType::CHANNEL_TOGGLE_READ_ACTIONS: if (ch.hasActions) toggleReadActions(ch, treatRecsAsLoops, seqIsRunning); break; case EventDispatcher::EventType::CHANNEL_KILL_READ_ACTIONS: /* Killing Read Actions, i.e. shift + click on 'R' button is meaningful only when the conf::treatRecsAsLoops is true. */ if (treatRecsAsLoops) killReadActions(ch); break; default: break; } } /* -------------------------------------------------------------------------- */ void SampleActionRecorder::record(Channel& ch, int note) const { m_actionRecorder->liveRec(ch.id, MidiEvent(note, 0, 0), m_sequencer->getCurrentFrameQuantized()); ch.hasActions = true; } /* -------------------------------------------------------------------------- */ void SampleActionRecorder::onKeyPress(Channel& ch) const { 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. */ if (ch.samplePlayer->mode == SamplePlayerMode::SINGLE_PRESS) ch.shared->readActions.store(false); } /* -------------------------------------------------------------------------- */ void SampleActionRecorder::startReadActions(Channel& ch, bool treatRecsAsLoops) const { if (treatRecsAsLoops) ch.shared->recStatus.store(ChannelStatus::WAIT); else { ch.shared->recStatus.store(ChannelStatus::PLAY); ch.shared->readActions.store(true); } } /* -------------------------------------------------------------------------- */ void SampleActionRecorder::stopReadActions(Channel& ch, ChannelStatus curRecStatus, bool treatRecsAsLoops, bool seqIsRunning) const { /* First of all, if the sequencer 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 (!seqIsRunning || !treatRecsAsLoops) { ch.shared->recStatus.store(ChannelStatus::OFF); ch.shared->readActions.store(false); } else if (curRecStatus == ChannelStatus::WAIT) ch.shared->recStatus.store(ChannelStatus::OFF); else if (curRecStatus == ChannelStatus::ENDING) ch.shared->recStatus.store(ChannelStatus::PLAY); else ch.shared->recStatus.store(ChannelStatus::ENDING); } /* -------------------------------------------------------------------------- */ void SampleActionRecorder::toggleReadActions(Channel& ch, bool treatRecsAsLoops, bool seqIsRunning) const { /* When you start reading actions while conf::treatRecsAsLoops is true, the value ch.shared->readActions actually is not set to true immediately, because the channel is in wait mode (REC_WAITING). readActions will become true on the next first beat. So a 'stop rec' command should occur also when readActions is false but the channel is in wait mode; this check will handle the case of when you press 'R', the channel goes into REC_WAITING and then you press 'R' again to undo the status. */ const bool readActions = ch.shared->readActions.load(); const ChannelStatus recStatus = ch.shared->recStatus.load(); if (readActions || (!readActions && recStatus == ChannelStatus::WAIT)) stopReadActions(ch, recStatus, treatRecsAsLoops, seqIsRunning); else startReadActions(ch, treatRecsAsLoops); } /* -------------------------------------------------------------------------- */ void SampleActionRecorder::killReadActions(Channel& ch) const { ch.shared->recStatus.store(ChannelStatus::OFF); ch.shared->readActions.store(false); } } // namespace giada::mgiada-0.22.0/src/core/channels/sampleActionRecorder.h000066400000000000000000000037621425106661500224460ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_CHANNEL_SAMPLE_ACTION_RECORDER_H #define G_CHANNEL_SAMPLE_ACTION_RECORDER_H #include "core/eventDispatcher.h" namespace giada::m { class ActionRecorder; class Sequencer; class Channel; class SampleActionRecorder final { public: SampleActionRecorder(ActionRecorder&, Sequencer&); void react(Channel&, const EventDispatcher::Event&, bool treatRecsAsLoops, bool seqIsRunning, bool canRecordActions) const; private: void record(Channel&, int note) const; void onKeyPress(Channel&) const; void startReadActions(Channel&, bool treatRecsAsLoops) const; void stopReadActions(Channel&, ChannelStatus, bool treatRecsAsLoops, bool seqIsRunning) const; void toggleReadActions(Channel&, bool treatRecsAsLoops, bool seqIsRunning) const; void killReadActions(Channel& ch) const; ActionRecorder* m_actionRecorder; Sequencer* m_sequencer; }; } // namespace giada::m #endif giada-0.22.0/src/core/channels/sampleAdvancer.cpp000066400000000000000000000144031425106661500216130ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * 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 * . * * -------------------------------------------------------------------------- */ #include "core/channels/sampleAdvancer.h" #include "core/channels/channel.h" #include namespace giada::m { void SampleAdvancer::onLastFrame(const Channel& ch, bool seqIsRunning, bool natural) const { const SamplePlayerMode mode = ch.samplePlayer->mode; const bool isLoop = ch.samplePlayer->isAnyLoopMode(); switch (ch.shared->playStatus.load()) { case ChannelStatus::PLAY: /* Stop LOOP_* when the sequencer is off, or SINGLE_* except for SINGLE_ENDLESS, which runs forever unless it's in ENDING mode. Other loop once modes are put in wait mode. */ if ((mode == SamplePlayerMode::SINGLE_BASIC || mode == SamplePlayerMode::SINGLE_BASIC_PAUSE || mode == SamplePlayerMode::SINGLE_PRESS || mode == SamplePlayerMode::SINGLE_RETRIG) || (isLoop && !seqIsRunning) || !natural) ch.shared->playStatus.store(ChannelStatus::OFF); else if (mode == SamplePlayerMode::LOOP_ONCE || mode == SamplePlayerMode::LOOP_ONCE_BAR) ch.shared->playStatus.store(ChannelStatus::WAIT); break; case ChannelStatus::ENDING: ch.shared->playStatus.store(ChannelStatus::OFF); break; default: break; } } /* -------------------------------------------------------------------------- */ void SampleAdvancer::advance(const Channel& ch, const Sequencer::Event& e) const { 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: if (ch.samplePlayer->isAnyLoopMode()) rewind(ch, e.delta); break; case Sequencer::EventType::ACTIONS: if (ch.shared->readActions.load() == true) parseActions(ch, *e.actions, e.delta); break; default: break; } } /* -------------------------------------------------------------------------- */ void SampleAdvancer::rewind(const Channel& ch, Frame localFrame) const { ch.shared->renderQueue->push({SamplePlayer::Render::Mode::REWIND, localFrame}); } /* -------------------------------------------------------------------------- */ void SampleAdvancer::stop(const Channel& ch, Frame localFrame) const { ch.shared->renderQueue->push({SamplePlayer::Render::Mode::STOP, localFrame}); } /* -------------------------------------------------------------------------- */ void SampleAdvancer::play(const Channel& ch, Frame localFrame) const { ch.shared->playStatus.store(ChannelStatus::PLAY); ch.shared->renderQueue->push({SamplePlayer::Render::Mode::NORMAL, localFrame}); } /* -------------------------------------------------------------------------- */ void SampleAdvancer::onFirstBeat(const Channel& ch, Frame localFrame) const { G_DEBUG("onFirstBeat ch=" << ch.id << ", localFrame=" << localFrame); const ChannelStatus playStatus = ch.shared->playStatus.load(); const ChannelStatus recStatus = ch.shared->recStatus.load(); const 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.shared->recStatus.store(ChannelStatus::PLAY); ch.shared->readActions.store(true); break; case ChannelStatus::ENDING: ch.shared->recStatus.store(ChannelStatus::OFF); ch.shared->readActions.store(false); break; default: break; } } /* -------------------------------------------------------------------------- */ void SampleAdvancer::onBar(const Channel& ch, Frame localFrame) const { G_DEBUG("onBar ch=" << ch.id << ", localFrame=" << localFrame); const ChannelStatus playStatus = ch.shared->playStatus.load(); const 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) play(ch, localFrame); } /* -------------------------------------------------------------------------- */ void SampleAdvancer::onNoteOn(const Channel& ch, Frame localFrame) const { switch (ch.shared->playStatus.load()) { case ChannelStatus::OFF: play(ch, localFrame); break; case ChannelStatus::PLAY: if (ch.samplePlayer->mode == SamplePlayerMode::SINGLE_RETRIG) rewind(ch, localFrame); else stop(ch, localFrame); break; default: break; } } /* -------------------------------------------------------------------------- */ void SampleAdvancer::parseActions(const Channel& ch, const std::vector& as, Frame localFrame) const { 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: case MidiEvent::NOTE_KILL: if (ch.shared->playStatus.load() == ChannelStatus::PLAY) stop(ch, localFrame); break; default: break; } } } } // namespace giada::mgiada-0.22.0/src/core/channels/sampleAdvancer.h000066400000000000000000000036121425106661500212600ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * 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 * . * * -------------------------------------------------------------------------- */ #ifndef G_CHANNEL_SAMPLE_ADVANCER_H #define G_CHANNEL_SAMPLE_ADVANCER_H #include "core/sequencer.h" namespace giada::m { class Channel; class SampleAdvancer final { public: void onLastFrame(const Channel& ch, bool seqIsRunning, bool natural) const; void advance(const Channel& ch, const Sequencer::Event& e) const; private: void rewind(const Channel& ch, Frame localFrame) const; void stop(const Channel& ch, Frame localFrame) const; void play(const Channel& ch, Frame localFrame) const; void onFirstBeat(const Channel& ch, Frame localFrame) const; void onBar(const Channel& ch, Frame localFrame) const; void onNoteOn(const Channel& ch, Frame localFrame) const; void parseActions(const Channel& ch, const std::vector& as, Frame localFrame) const; }; } // namespace giada::m #endif giada-0.22.0/src/core/channels/samplePlayer.cpp000066400000000000000000000157441425106661500213350ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "samplePlayer.h" #include "core/channels/channel.h" #include "core/wave.h" #include "deps/mcl-audio-buffer/src/audioBuffer.hpp" #include #include using namespace mcl; namespace giada::m { SamplePlayer::SamplePlayer(Resampler* r) : pitch(G_DEFAULT_PITCH) , mode(SamplePlayerMode::SINGLE_BASIC) , shift(0) , begin(0) , end(0) , velocityAsVol(false) , waveReader(r) { } /* -------------------------------------------------------------------------- */ SamplePlayer::SamplePlayer(const Patch::Channel& p, float samplerateRatio, Resampler* r, Wave* w) : pitch(p.pitch) , mode(p.mode) , shift(p.shift) , begin(p.begin) , end(p.end) , velocityAsVol(p.midiInVeloAsVol) , waveReader(r) , onLastFrame(nullptr) { setWave(w, samplerateRatio); } /* -------------------------------------------------------------------------- */ bool SamplePlayer::hasWave() const { return waveReader.wave != nullptr; } bool SamplePlayer::hasLogicalWave() const { return hasWave() && waveReader.wave->isLogical(); } bool SamplePlayer::hasEditedWave() const { return hasWave() && waveReader.wave->isEdited(); } /* -------------------------------------------------------------------------- */ bool SamplePlayer::isAnyLoopMode() const { return mode == SamplePlayerMode::LOOP_BASIC || mode == SamplePlayerMode::LOOP_ONCE || mode == SamplePlayerMode::LOOP_REPEAT || mode == SamplePlayerMode::LOOP_ONCE_BAR; } /* -------------------------------------------------------------------------- */ Wave* SamplePlayer::getWave() const { return waveReader.wave; } ID SamplePlayer::getWaveId() const { if (hasWave()) return waveReader.wave->id; return 0; } /* -------------------------------------------------------------------------- */ Frame SamplePlayer::getWaveSize() const { return hasWave() ? waveReader.wave->getBuffer().countFrames() : 0; } /* -------------------------------------------------------------------------- */ void SamplePlayer::react(const EventDispatcher::Event& e) { if (e.type == EventDispatcher::EventType::CHANNEL_PITCH) pitch = std::get(e.data); } /* -------------------------------------------------------------------------- */ void SamplePlayer::render(ChannelShared& shared, Render renderInfo) const { if (waveReader.wave == nullptr) return; AudioBuffer& buf = shared.audioBuffer; Frame tracker = std::clamp(shared.tracker.load(), begin, end); /* Make sure tracker stays within begin-end range. */ const ChannelStatus status = shared.playStatus.load(); if (renderInfo.mode == Render::Mode::NORMAL) { tracker = render(buf, tracker, renderInfo.offset, status); } else { /* Both modes: 1st = [abcdefghijklmnopq] No need for fancy render() here. You don't want the chance to trigger onLastFrame() at this point which would invalidate the rewind (a listener might stop the rendering): fillBuffer() is just enough. Just notify waveReader this is the last read before rewind. */ tracker = fillBuffer(buf, tracker, 0).used; waveReader.last(); /* Mode::REWIND: 2nd = [abcdefghi|abcdfefg] Mode::STOP: 2nd = [abcdefghi|--------] */ if (renderInfo.mode == Render::Mode::REWIND) tracker = render(buf, begin, renderInfo.offset, status); else tracker = stop(buf, renderInfo.offset); } shared.tracker.store(tracker); } /* -------------------------------------------------------------------------- */ Frame SamplePlayer::render(AudioBuffer& buf, Frame tracker, Frame offset, ChannelStatus status) const { /* First pass rendering. */ WaveReader::Result res = fillBuffer(buf, tracker, offset); tracker += res.used; /* Second pass rendering: if tracker has looped, special care is needed. If the channel is in loop mode, fill the second part of the buffer with data coming from the sample's head, starting at 'res.generated' offset. */ if (tracker >= end) { assert(onLastFrame != nullptr); tracker = begin; waveReader.last(); onLastFrame(/*natural=*/true); if (shouldLoop(status) && res.generated < buf.countFrames()) tracker += fillBuffer(buf, tracker, res.generated).used; } return tracker; } /* -------------------------------------------------------------------------- */ Frame SamplePlayer::stop(AudioBuffer& buf, Frame offset) const { assert(onLastFrame != nullptr); onLastFrame(/*natural=*/false); if (offset != 0) buf.clear(offset); return begin; } /* -------------------------------------------------------------------------- */ void SamplePlayer::loadWave(ChannelShared& shared, Wave* w) { waveReader.wave = w; shared.tracker.store(0); shared.playStatus.store(w != nullptr ? ChannelStatus::OFF : ChannelStatus::EMPTY); shift = 0; begin = 0; end = w != nullptr ? w->getBuffer().countFrames() - 1 : 0; } /* -------------------------------------------------------------------------- */ void SamplePlayer::setWave(Wave* w, float samplerateRatio) { if (w == nullptr) { waveReader.wave = nullptr; return; } waveReader.wave = w; if (samplerateRatio != 1.0f) { begin *= samplerateRatio; end *= samplerateRatio; shift *= samplerateRatio; } } /* -------------------------------------------------------------------------- */ void SamplePlayer::kickIn(ChannelShared& shared, Frame f) { shared.tracker.store(f); shared.playStatus.store(ChannelStatus::PLAY); } /* -------------------------------------------------------------------------- */ WaveReader::Result SamplePlayer::fillBuffer(AudioBuffer& buf, Frame start, Frame offset) const { return waveReader.fill(buf, start, end, offset, pitch); } /* -------------------------------------------------------------------------- */ bool SamplePlayer::shouldLoop(ChannelStatus status) const { return (mode == SamplePlayerMode::LOOP_BASIC || mode == SamplePlayerMode::LOOP_REPEAT || mode == SamplePlayerMode::SINGLE_ENDLESS) && status == ChannelStatus::PLAY; // Don't loop if ENDING } } // namespace giada::mgiada-0.22.0/src/core/channels/samplePlayer.h000066400000000000000000000101121425106661500207620ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_CHANNEL_SAMPLE_PLAYER_H #define G_CHANNEL_SAMPLE_PLAYER_H #include "core/channels/waveReader.h" #include "core/const.h" #include "core/eventDispatcher.h" #include "core/patch.h" #include "core/sequencer.h" #include "core/types.h" #include namespace giada::m { struct ChannelShared; class Channel; class SamplePlayer final { public: /* Render Determines how the render() function should behave. Mode::NORMAL - normal rendering, starting at offset 'offset'; Mode::REWIND - two-step rendering, used when the sample must rewind at some point ('offset') in the audio buffer; Mode::STOP - abort rendering. The audio buffer is silenced starting at 'offset'. Also triggers onLastFrame(). */ struct Render { enum class Mode { NORMAL, REWIND, STOP }; Mode mode = Mode::NORMAL; Frame offset = 0; }; SamplePlayer(Resampler* r); SamplePlayer(const Patch::Channel& p, float samplerateRatio, Resampler* r, Wave* w); bool hasWave() const; bool hasLogicalWave() const; bool hasEditedWave() const; bool isAnyLoopMode() const; ID getWaveId() const; Frame getWaveSize() const; Wave* getWave() const; void render(ChannelShared&, Render) const; void react(const EventDispatcher::Event& e); /* loadWave Loads Wave and sets it up (name, markers, ...). Also updates Channel's shared state accordingly. */ void loadWave(ChannelShared&, Wave*); /* 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. */ void setWave(Wave* w, float samplerateRatio); /* kickIn Starts the player right away at frame 'f'. Used when launching a loop after being live recorded. */ void kickIn(ChannelShared&, Frame f); float pitch; SamplePlayerMode mode; Frame shift; Frame begin; Frame end; bool velocityAsVol; // Velocity drives volume WaveReader waveReader; /* onLastFrame Callback fired when the last frame has been reached. 'natural' == true if the rendering has ended because the end of the sample has ben reached. 'natural' == false if the rendering has been manually interrupted (by a Render::Mode::STOP type). */ std::function onLastFrame; private: /* render Renders audio into the buffer. Reads audio data from 'tracker' and copies it into the audio buffer at position 'offset'. May fire 'onLastFrame' callback if the sample end is reached. */ Frame render(mcl::AudioBuffer&, Frame tracker, Frame offset, ChannelStatus) const; /* stop Silences the last part of the audio buffer, starting at 'offset'. Used to terminate rendering. It also fire the 'onLastFrame' callback. */ Frame stop(mcl::AudioBuffer&, Frame offset) const; WaveReader::Result fillBuffer(mcl::AudioBuffer&, Frame start, Frame offset) const; bool shouldLoop(ChannelStatus) const; }; } // namespace giada::m #endif giada-0.22.0/src/core/channels/sampleReactor.cpp000066400000000000000000000155351425106661500214760ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * 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 * . * * -------------------------------------------------------------------------- */ #include "core/channels/sampleReactor.h" #include "core/channels/channel.h" #include "core/conf.h" #include "core/model/model.h" #include "utils/math.h" namespace giada::m { namespace { constexpr int Q_ACTION_PLAY = 0; constexpr int Q_ACTION_REWIND = 10000; // Avoid clash with Q_ACTION_PLAY + channelId } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ SampleReactor::SampleReactor(Channel& ch, ID channelId) { ch.shared->quantizer->schedule(Q_ACTION_PLAY + channelId, [this, shared = ch.shared](Frame delta) { play(*shared, delta); }); ch.shared->quantizer->schedule(Q_ACTION_REWIND + channelId, [this, shared = ch.shared](Frame delta) { ChannelStatus status = shared->playStatus.load(); if (status == ChannelStatus::OFF) play(*shared, delta); else if (status == ChannelStatus::PLAY || status == ChannelStatus::ENDING) rewind(*shared, delta); }); } /* -------------------------------------------------------------------------- */ void SampleReactor::react(Channel& ch, const EventDispatcher::Event& e, bool chansStopOnSeqHalt, bool canQuantize) const { if (!ch.hasWave()) return; switch (e.type) { case EventDispatcher::EventType::KEY_PRESS: press(ch, std::get(e.data), canQuantize); break; case EventDispatcher::EventType::KEY_RELEASE: release(ch); break; case EventDispatcher::EventType::KEY_KILL: if (ch.shared->playStatus.load() == ChannelStatus::PLAY) stop(*ch.shared); break; case EventDispatcher::EventType::SEQUENCER_STOP: onStopBySeq(ch, chansStopOnSeqHalt); break; default: break; } } /* -------------------------------------------------------------------------- */ void SampleReactor::rewind(ChannelShared& shared, Frame localFrame) const { shared.renderQueue->push({SamplePlayer::Render::Mode::REWIND, localFrame}); } /* -------------------------------------------------------------------------- */ void SampleReactor::play(ChannelShared& shared, Frame localFrame) const { shared.playStatus.store(ChannelStatus::PLAY); shared.renderQueue->push({SamplePlayer::Render::Mode::NORMAL, localFrame}); } /* -------------------------------------------------------------------------- */ void SampleReactor::stop(ChannelShared& shared) const { shared.renderQueue->push({SamplePlayer::Render::Mode::STOP, 0}); } /* -------------------------------------------------------------------------- */ ChannelStatus SampleReactor::pressWhileOff(Channel& ch, int velocity, bool isLoop, bool canQuantize) const { if (isLoop) return ChannelStatus::WAIT; if (ch.samplePlayer->velocityAsVol) ch.volume_i = u::math::map(velocity, G_MAX_VELOCITY, G_MAX_VOLUME); if (canQuantize) { ch.shared->quantizer->trigger(Q_ACTION_PLAY + ch.id); return ChannelStatus::OFF; } else return ChannelStatus::PLAY; } /* -------------------------------------------------------------------------- */ ChannelStatus SampleReactor::pressWhilePlay(Channel& ch, SamplePlayerMode mode, bool isLoop, bool canQuantize) const { if (isLoop) return ChannelStatus::ENDING; switch (mode) { case SamplePlayerMode::SINGLE_RETRIG: if (canQuantize) ch.shared->quantizer->trigger(Q_ACTION_REWIND + ch.id); else rewind(*ch.shared, /*localFrame=*/0); return ChannelStatus::PLAY; case SamplePlayerMode::SINGLE_ENDLESS: return ChannelStatus::ENDING; case SamplePlayerMode::SINGLE_BASIC: stop(*ch.shared); return ChannelStatus::PLAY; // Let SamplePlayer stop it once done default: return ChannelStatus::OFF; } } /* -------------------------------------------------------------------------- */ void SampleReactor::press(Channel& ch, int velocity, bool canQuantize) const { const SamplePlayerMode mode = ch.samplePlayer->mode; const bool isLoop = ch.samplePlayer->isAnyLoopMode(); ChannelStatus playStatus = ch.shared->playStatus.load(); switch (playStatus) { case ChannelStatus::OFF: playStatus = pressWhileOff(ch, velocity, isLoop, canQuantize); break; case ChannelStatus::PLAY: playStatus = pressWhilePlay(ch, mode, isLoop, canQuantize); break; case ChannelStatus::WAIT: playStatus = ChannelStatus::OFF; break; case ChannelStatus::ENDING: playStatus = ChannelStatus::PLAY; break; default: break; } ch.shared->playStatus.store(playStatus); } /* -------------------------------------------------------------------------- */ void SampleReactor::release(Channel& ch) const { /* 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.shared->playStatus.load() == ChannelStatus::PLAY) stop(*ch.shared); // Let SamplePlayer stop it once done else if (ch.shared->quantizer->hasBeenTriggered()) ch.shared->quantizer->clear(); } /* -------------------------------------------------------------------------- */ void SampleReactor::onStopBySeq(Channel& ch, bool chansStopOnSeqHalt) const { G_DEBUG("onStopBySeq ch=" << ch.id); ChannelStatus playStatus = ch.shared->playStatus.load(); bool isReadingActions = ch.shared->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.shared->playStatus.store(ChannelStatus::OFF); break; case ChannelStatus::PLAY: if (chansStopOnSeqHalt && (isLoop || isReadingActions)) stop(*ch.shared); break; default: break; } } } // namespace giada::mgiada-0.22.0/src/core/channels/sampleReactor.h000066400000000000000000000043601425106661500211350ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * 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 * . * * -------------------------------------------------------------------------- */ #ifndef G_CHANNEL_SAMPLE_REACTOR_H #define G_CHANNEL_SAMPLE_REACTOR_H #include "core/conf.h" #include "core/eventDispatcher.h" namespace giada::m::model { class Model; } namespace giada::m { class Channel; struct ChannelShared; /* SampleReactor Reacts to manual events sent to Sample Channels: key press, key release, sequencer stop, ... . */ class SampleReactor final { public: struct Event { int type; Frame offset; }; SampleReactor(Channel&, ID channelId); void react(Channel&, const EventDispatcher::Event&, bool chansStopOnSeqHalt, bool canQuantize) const; private: void onStopBySeq(Channel&, bool chansStopOnSeqHalt) const; void release(Channel&) const; void press(Channel&, int velocity, bool canQuantize) const; ChannelStatus pressWhilePlay(Channel&, SamplePlayerMode, bool isLoop, bool canQuantize) const; ChannelStatus pressWhileOff(Channel&, int velocity, bool isLoop, bool canQuantize) const; void rewind(ChannelShared&, Frame localFrame) const; void play(ChannelShared&, Frame localFrame) const; void stop(ChannelShared&) const; }; } // namespace giada::m #endif giada-0.22.0/src/core/channels/waveReader.cpp000066400000000000000000000056031425106661500207550ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "waveReader.h" #include "core/const.h" #include "core/model/model.h" #include "core/wave.h" #include "deps/mcl-audio-buffer/src/audioBuffer.hpp" #include "utils/log.h" #include #include #include namespace giada::m { WaveReader::WaveReader(Resampler* r) : wave(nullptr) , m_resampler(r) { } /* -------------------------------------------------------------------------- */ WaveReader::Result WaveReader::fill(mcl::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()); if (pitch == 1.0f) return fillCopy(out, start, max, offset); else return fillResampled(out, start, max, offset, pitch); } /* -------------------------------------------------------------------------- */ WaveReader::Result WaveReader::fillResampled(mcl::AudioBuffer& dest, Frame start, Frame max, Frame offset, float pitch) const { 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(res.used), static_cast(res.generated)}; } /* -------------------------------------------------------------------------- */ WaveReader::Result WaveReader::fillCopy(mcl::AudioBuffer& dest, Frame start, Frame max, Frame offset) const { Frame used = dest.countFrames() - offset; if (used > max - start) used = max - start; dest.set(wave->getBuffer(), used, start, offset); return {used, used}; } void WaveReader::last() const { if (m_resampler != nullptr) m_resampler->last(); } } // namespace giada::m giada-0.22.0/src/core/channels/waveReader.h000066400000000000000000000047331425106661500204250ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_CHANNEL_WAVE_READER_H #define G_CHANNEL_WAVE_READER_H #include "core/types.h" namespace mcl { class AudioBuffer; } namespace giada::m { class Wave; 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). */ struct Result { Frame used, generated; }; WaveReader() = delete; WaveReader(Resampler* r); /* 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'. */ Result fill(mcl::AudioBuffer& out, Frame start, Frame max, Frame offset, float pitch) const; /* last Call this when you are about to process the last chunk of pitched data. Ignored if pitch == 1.0. */ void last() const; /* wave Wave object. Might be null if the channel has no sample. */ Wave* wave; private: Result fillResampled(mcl::AudioBuffer& out, Frame start, Frame max, Frame offset, float pitch) const; Result fillCopy(mcl::AudioBuffer& out, Frame start, Frame max, Frame offset) const; Resampler* m_resampler; }; } // namespace giada::m #endif giada-0.22.0/src/core/conf.cpp000066400000000000000000000377241425106661500160330ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/conf.h" #include "core/const.h" #include "core/types.h" #include "utils/fs.h" #include "utils/log.h" #include #include #include #include #include namespace nl = nlohmann; namespace giada::m { Conf::Conf() { m_confFilePath = u::fs::join(u::fs::getHomePath(), CONF_FILENAME); m_confDirPath = u::fs::getHomePath(); } /* -------------------------------------------------------------------------- */ bool Conf::read() { data = Data(); // Reset it first std::ifstream ifs(m_confFilePath); if (!ifs.good()) return false; nl::json j = nl::json::parse(ifs); data.logMode = j.value(CONF_KEY_LOG_MODE, data.logMode); data.showTooltips = j.value(CONF_KEY_SHOW_TOOLTIPS, data.showTooltips); data.langMap = j.value(CONF_KEY_LANGMAP, data.langMap); data.soundSystem = j.value(CONF_KEY_SOUND_SYSTEM, data.soundSystem); data.soundDeviceOut = j.value(CONF_KEY_SOUND_DEVICE_OUT, data.soundDeviceOut); data.soundDeviceIn = j.value(CONF_KEY_SOUND_DEVICE_IN, data.soundDeviceIn); data.channelsOutCount = j.value(CONF_KEY_CHANNELS_OUT_COUNT, data.channelsOutCount); data.channelsOutStart = j.value(CONF_KEY_CHANNELS_OUT_START, data.channelsOutStart); data.channelsInCount = j.value(CONF_KEY_CHANNELS_IN_COUNT, data.channelsInCount); data.channelsInStart = j.value(CONF_KEY_CHANNELS_IN_START, data.channelsInStart); data.samplerate = j.value(CONF_KEY_SAMPLERATE, data.samplerate); data.buffersize = j.value(CONF_KEY_BUFFER_SIZE, data.buffersize); data.limitOutput = j.value(CONF_KEY_LIMIT_OUTPUT, data.limitOutput); data.rsmpQuality = j.value(CONF_KEY_RESAMPLE_QUALITY, data.rsmpQuality); data.midiSystem = j.value(CONF_KEY_MIDI_SYSTEM, data.midiSystem); data.midiPortOut = j.value(CONF_KEY_MIDI_PORT_OUT, data.midiPortOut); data.midiPortIn = j.value(CONF_KEY_MIDI_PORT_IN, data.midiPortIn); data.midiMapPath = j.value(CONF_KEY_MIDIMAP_PATH, data.midiMapPath); data.lastFileMap = j.value(CONF_KEY_LAST_MIDIMAP, data.lastFileMap); data.midiSync = j.value(CONF_KEY_MIDI_SYNC, data.midiSync); data.midiTCfps = j.value(CONF_KEY_MIDI_TC_FPS, data.midiTCfps); data.chansStopOnSeqHalt = j.value(CONF_KEY_CHANS_STOP_ON_SEQ_HALT, data.chansStopOnSeqHalt); data.treatRecsAsLoops = j.value(CONF_KEY_TREAT_RECS_AS_LOOPS, data.treatRecsAsLoops); data.inputMonitorDefaultOn = j.value(CONF_KEY_INPUT_MONITOR_DEFAULT_ON, data.inputMonitorDefaultOn); data.overdubProtectionDefaultOn = j.value(CONF_KEY_OVERDUB_PROTECTION_DEFAULT_ON, data.overdubProtectionDefaultOn); data.pluginPath = j.value(CONF_KEY_PLUGINS_PATH, data.pluginPath); data.patchPath = j.value(CONF_KEY_PATCHES_PATH, data.patchPath); data.samplePath = j.value(CONF_KEY_SAMPLES_PATH, data.samplePath); data.mainWindowX = j.value(CONF_KEY_MAIN_WINDOW_X, data.mainWindowX); data.mainWindowY = j.value(CONF_KEY_MAIN_WINDOW_Y, data.mainWindowY); data.mainWindowW = j.value(CONF_KEY_MAIN_WINDOW_W, data.mainWindowW); data.mainWindowH = j.value(CONF_KEY_MAIN_WINDOW_H, data.mainWindowH); data.browserX = j.value(CONF_KEY_BROWSER_X, data.browserX); data.browserY = j.value(CONF_KEY_BROWSER_Y, data.browserY); data.browserW = j.value(CONF_KEY_BROWSER_W, data.browserW); data.browserH = j.value(CONF_KEY_BROWSER_H, data.browserH); data.browserPosition = j.value(CONF_KEY_BROWSER_POSITION, data.browserPosition); data.browserLastPath = j.value(CONF_KEY_BROWSER_LAST_PATH, data.browserLastPath); data.browserLastValue = j.value(CONF_KEY_BROWSER_LAST_VALUE, data.browserLastValue); data.actionEditorX = j.value(CONF_KEY_ACTION_EDITOR_X, data.actionEditorX); data.actionEditorY = j.value(CONF_KEY_ACTION_EDITOR_Y, data.actionEditorY); data.actionEditorW = j.value(CONF_KEY_ACTION_EDITOR_W, data.actionEditorW); data.actionEditorH = j.value(CONF_KEY_ACTION_EDITOR_H, data.actionEditorH); data.actionEditorZoom = j.value(CONF_KEY_ACTION_EDITOR_ZOOM, data.actionEditorZoom); data.actionEditorSplitH = j.value(CONF_KEY_ACTION_EDITOR_SPLIT_H, data.actionEditorSplitH); data.actionEditorGridVal = j.value(CONF_KEY_ACTION_EDITOR_GRID_VAL, data.actionEditorGridVal); data.actionEditorGridOn = j.value(CONF_KEY_ACTION_EDITOR_GRID_ON, data.actionEditorGridOn); data.actionEditorPianoRollY = j.value(CONF_KEY_ACTION_EDITOR_PIANO_ROLL_Y, data.actionEditorPianoRollY); data.sampleEditorX = j.value(CONF_KEY_SAMPLE_EDITOR_X, data.sampleEditorX); data.sampleEditorY = j.value(CONF_KEY_SAMPLE_EDITOR_Y, data.sampleEditorY); data.sampleEditorW = j.value(CONF_KEY_SAMPLE_EDITOR_W, data.sampleEditorW); data.sampleEditorH = j.value(CONF_KEY_SAMPLE_EDITOR_H, data.sampleEditorH); data.sampleEditorGridVal = j.value(CONF_KEY_SAMPLE_EDITOR_GRID_VAL, data.sampleEditorGridVal); data.sampleEditorGridOn = j.value(CONF_KEY_SAMPLE_EDITOR_GRID_ON, data.sampleEditorGridOn); data.pluginListX = j.value(CONF_KEY_PLUGIN_LIST_X, data.pluginListX); data.pluginListY = j.value(CONF_KEY_PLUGIN_LIST_Y, data.pluginListY); data.midiInputX = j.value(CONF_KEY_MIDI_INPUT_X, data.midiInputX); data.midiInputY = j.value(CONF_KEY_MIDI_INPUT_Y, data.midiInputY); data.midiInputW = j.value(CONF_KEY_MIDI_INPUT_W, data.midiInputW); data.midiInputH = j.value(CONF_KEY_MIDI_INPUT_H, data.midiInputH); data.recTriggerMode = j.value(CONF_KEY_REC_TRIGGER_MODE, data.recTriggerMode); data.recTriggerLevel = j.value(CONF_KEY_REC_TRIGGER_LEVEL, data.recTriggerLevel); data.inputRecMode = j.value(CONF_KEY_INPUT_REC_MODE, data.inputRecMode); data.midiInEnabled = j.value(CONF_KEY_MIDI_IN, data.midiInEnabled); data.midiInFilter = j.value(CONF_KEY_MIDI_IN_FILTER, data.midiInFilter); data.midiInRewind = j.value(CONF_KEY_MIDI_IN_REWIND, data.midiInRewind); data.midiInStartStop = j.value(CONF_KEY_MIDI_IN_START_STOP, data.midiInStartStop); data.midiInActionRec = j.value(CONF_KEY_MIDI_IN_ACTION_REC, data.midiInActionRec); data.midiInInputRec = j.value(CONF_KEY_MIDI_IN_INPUT_REC, data.midiInInputRec); data.midiInMetronome = j.value(CONF_KEY_MIDI_IN_METRONOME, data.midiInMetronome); data.midiInVolumeIn = j.value(CONF_KEY_MIDI_IN_VOLUME_IN, data.midiInVolumeIn); data.midiInVolumeOut = j.value(CONF_KEY_MIDI_IN_VOLUME_OUT, data.midiInVolumeOut); data.midiInBeatDouble = j.value(CONF_KEY_MIDI_IN_BEAT_DOUBLE, data.midiInBeatDouble); data.midiInBeatHalf = j.value(CONF_KEY_MIDI_IN_BEAT_HALF, data.midiInBeatHalf); data.keyBindings[KEY_BIND_PLAY] = j.value(CONF_KEY_BIND_PLAY, 0); data.keyBindings[KEY_BIND_REWIND] = j.value(CONF_KEY_BIND_REWIND, 0); data.keyBindings[KEY_BIND_RECORD_ACTIONS] = j.value(CONF_KEY_BIND_RECORD_ACTIONS, 0); data.keyBindings[KEY_BIND_RECORD_INPUT] = j.value(CONF_KEY_BIND_RECORD_INPUT, 0); data.keyBindings[KEY_BIND_EXIT] = j.value(CONF_KEY_BIND_EXIT, 0); #ifdef WITH_VST data.pluginChooserX = j.value(CONF_KEY_PLUGIN_CHOOSER_X, data.pluginChooserX); data.pluginChooserY = j.value(CONF_KEY_PLUGIN_CHOOSER_Y, data.pluginChooserY); data.pluginChooserW = j.value(CONF_KEY_PLUGIN_CHOOSER_W, data.pluginChooserW); data.pluginChooserH = j.value(CONF_KEY_PLUGIN_CHOOSER_H, data.pluginChooserH); data.pluginSortMethod = j.value(CONF_KEY_PLUGIN_SORT_METHOD, data.pluginSortMethod); #endif sanitize(); return true; } /* -------------------------------------------------------------------------- */ bool Conf::write() const { if (!createConfigFolder()) return false; nl::json j; j[CONF_KEY_HEADER] = "GIADACFG"; j[CONF_KEY_LOG_MODE] = data.logMode; j[CONF_KEY_SHOW_TOOLTIPS] = data.showTooltips; j[CONF_KEY_LANGMAP] = data.langMap; j[CONF_KEY_SOUND_SYSTEM] = data.soundSystem; j[CONF_KEY_SOUND_DEVICE_OUT] = data.soundDeviceOut; j[CONF_KEY_SOUND_DEVICE_IN] = data.soundDeviceIn; j[CONF_KEY_CHANNELS_OUT_COUNT] = data.channelsOutCount; j[CONF_KEY_CHANNELS_OUT_START] = data.channelsOutStart; j[CONF_KEY_CHANNELS_IN_COUNT] = data.channelsInCount; j[CONF_KEY_CHANNELS_IN_START] = data.channelsInStart; j[CONF_KEY_SAMPLERATE] = data.samplerate; j[CONF_KEY_BUFFER_SIZE] = data.buffersize; j[CONF_KEY_LIMIT_OUTPUT] = data.limitOutput; j[CONF_KEY_RESAMPLE_QUALITY] = data.rsmpQuality; j[CONF_KEY_MIDI_SYSTEM] = data.midiSystem; j[CONF_KEY_MIDI_PORT_OUT] = data.midiPortOut; j[CONF_KEY_MIDI_PORT_IN] = data.midiPortIn; j[CONF_KEY_MIDIMAP_PATH] = data.midiMapPath; j[CONF_KEY_LAST_MIDIMAP] = data.lastFileMap; j[CONF_KEY_MIDI_SYNC] = data.midiSync; j[CONF_KEY_MIDI_TC_FPS] = data.midiTCfps; j[CONF_KEY_MIDI_IN] = data.midiInEnabled; j[CONF_KEY_MIDI_IN_FILTER] = data.midiInFilter; j[CONF_KEY_MIDI_IN_REWIND] = data.midiInRewind; j[CONF_KEY_MIDI_IN_START_STOP] = data.midiInStartStop; j[CONF_KEY_MIDI_IN_ACTION_REC] = data.midiInActionRec; j[CONF_KEY_MIDI_IN_INPUT_REC] = data.midiInInputRec; j[CONF_KEY_MIDI_IN_METRONOME] = data.midiInMetronome; j[CONF_KEY_MIDI_IN_VOLUME_IN] = data.midiInVolumeIn; j[CONF_KEY_MIDI_IN_VOLUME_OUT] = data.midiInVolumeOut; j[CONF_KEY_MIDI_IN_BEAT_DOUBLE] = data.midiInBeatDouble; j[CONF_KEY_MIDI_IN_BEAT_HALF] = data.midiInBeatHalf; j[CONF_KEY_CHANS_STOP_ON_SEQ_HALT] = data.chansStopOnSeqHalt; j[CONF_KEY_TREAT_RECS_AS_LOOPS] = data.treatRecsAsLoops; j[CONF_KEY_INPUT_MONITOR_DEFAULT_ON] = data.inputMonitorDefaultOn; j[CONF_KEY_OVERDUB_PROTECTION_DEFAULT_ON] = data.overdubProtectionDefaultOn; j[CONF_KEY_PLUGINS_PATH] = data.pluginPath; j[CONF_KEY_PATCHES_PATH] = data.patchPath; j[CONF_KEY_SAMPLES_PATH] = data.samplePath; j[CONF_KEY_MAIN_WINDOW_X] = data.mainWindowX; j[CONF_KEY_MAIN_WINDOW_Y] = data.mainWindowY; j[CONF_KEY_MAIN_WINDOW_W] = data.mainWindowW; j[CONF_KEY_MAIN_WINDOW_H] = data.mainWindowH; j[CONF_KEY_BROWSER_X] = data.browserX; j[CONF_KEY_BROWSER_Y] = data.browserY; j[CONF_KEY_BROWSER_W] = data.browserW; j[CONF_KEY_BROWSER_H] = data.browserH; j[CONF_KEY_BROWSER_POSITION] = data.browserPosition; j[CONF_KEY_BROWSER_LAST_PATH] = data.browserLastPath; j[CONF_KEY_BROWSER_LAST_VALUE] = data.browserLastValue; j[CONF_KEY_ACTION_EDITOR_X] = data.actionEditorX; j[CONF_KEY_ACTION_EDITOR_Y] = data.actionEditorY; j[CONF_KEY_ACTION_EDITOR_W] = data.actionEditorW; j[CONF_KEY_ACTION_EDITOR_H] = data.actionEditorH; j[CONF_KEY_ACTION_EDITOR_ZOOM] = data.actionEditorZoom; j[CONF_KEY_ACTION_EDITOR_SPLIT_H] = data.actionEditorSplitH; j[CONF_KEY_ACTION_EDITOR_GRID_VAL] = data.actionEditorGridVal; j[CONF_KEY_ACTION_EDITOR_GRID_ON] = data.actionEditorGridOn; j[CONF_KEY_ACTION_EDITOR_PIANO_ROLL_Y] = data.actionEditorPianoRollY; j[CONF_KEY_SAMPLE_EDITOR_X] = data.sampleEditorX; j[CONF_KEY_SAMPLE_EDITOR_Y] = data.sampleEditorY; j[CONF_KEY_SAMPLE_EDITOR_W] = data.sampleEditorW; j[CONF_KEY_SAMPLE_EDITOR_H] = data.sampleEditorH; j[CONF_KEY_SAMPLE_EDITOR_GRID_VAL] = data.sampleEditorGridVal; j[CONF_KEY_SAMPLE_EDITOR_GRID_ON] = data.sampleEditorGridOn; j[CONF_KEY_PLUGIN_LIST_X] = data.pluginListX; j[CONF_KEY_PLUGIN_LIST_Y] = data.pluginListY; j[CONF_KEY_MIDI_INPUT_X] = data.midiInputX; j[CONF_KEY_MIDI_INPUT_Y] = data.midiInputY; j[CONF_KEY_MIDI_INPUT_W] = data.midiInputW; j[CONF_KEY_MIDI_INPUT_H] = data.midiInputH; j[CONF_KEY_REC_TRIGGER_MODE] = static_cast(data.recTriggerMode); j[CONF_KEY_REC_TRIGGER_LEVEL] = data.recTriggerLevel; j[CONF_KEY_INPUT_REC_MODE] = static_cast(data.inputRecMode); j[CONF_KEY_BIND_PLAY] = data.keyBindings[KEY_BIND_PLAY]; j[CONF_KEY_BIND_REWIND] = data.keyBindings[KEY_BIND_REWIND]; j[CONF_KEY_BIND_RECORD_ACTIONS] = data.keyBindings[KEY_BIND_RECORD_ACTIONS]; j[CONF_KEY_BIND_RECORD_INPUT] = data.keyBindings[KEY_BIND_RECORD_INPUT]; j[CONF_KEY_BIND_EXIT] = data.keyBindings[KEY_BIND_EXIT]; #ifdef WITH_VST j[CONF_KEY_PLUGIN_CHOOSER_X] = data.pluginChooserX; j[CONF_KEY_PLUGIN_CHOOSER_Y] = data.pluginChooserY; j[CONF_KEY_PLUGIN_CHOOSER_W] = data.pluginChooserW; j[CONF_KEY_PLUGIN_CHOOSER_H] = data.pluginChooserH; j[CONF_KEY_PLUGIN_SORT_METHOD] = data.pluginSortMethod; #endif std::ofstream ofs(m_confFilePath); if (!ofs.good()) { u::log::print("[conf::write] unable to write configuration file!\n"); return false; } ofs << j; return true; } /* -------------------------------------------------------------------------- */ bool Conf::createConfigFolder() const { #if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) if (u::fs::dirExists(m_confDirPath)) return true; u::log::print("[conf::createConfigFolder] .giada folder not present. Updating...\n"); if (u::fs::mkdir(m_confDirPath)) { u::log::print("[conf::createConfigFolder] status: ok\n"); return true; } else { u::log::print("[conf::createConfigFolder] status: error!\n"); return false; } #else // Windows: nothing to do return true; #endif } /* -------------------------------------------------------------------------- */ void Conf::sanitize() { data.soundDeviceOut = std::max(0, data.soundDeviceOut); data.channelsOutCount = G_MAX_IO_CHANS; data.channelsOutStart = std::max(0, data.channelsOutStart); data.channelsInCount = std::max(1, data.channelsInCount); data.channelsInStart = std::max(0, data.channelsInStart); data.midiPortOut = std::max(-1, data.midiPortOut); data.midiPortIn = std::max(-1, data.midiPortIn); } } // namespace giada::mgiada-0.22.0/src/core/conf.h000066400000000000000000000125761425106661500154760ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_CONF_H #define G_CONF_H #include "core/const.h" #include "core/types.h" #include "utils/gui.h" #include #include namespace giada::m { class Conf final { public: using KeyBindings = std::vector; static constexpr int KEY_BIND_PLAY = 0; static constexpr int KEY_BIND_REWIND = 1; static constexpr int KEY_BIND_RECORD_ACTIONS = 2; static constexpr int KEY_BIND_RECORD_INPUT = 3; static constexpr int KEY_BIND_EXIT = 4; struct Data { int logMode = LOG_MODE_MUTE; bool showTooltips = true; std::string langMap = ""; 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 midiPortIn = G_DEFAULT_MIDI_PORT_IN; std::string midiMapPath = ""; std::string lastFileMap = ""; int midiSync = G_MIDI_SYNC_NONE; float midiTCfps = 25.0f; bool chansStopOnSeqHalt = false; bool treatRecsAsLoops = false; bool inputMonitorDefaultOn = false; bool overdubProtectionDefaultOn = false; std::string pluginPath; std::string patchPath; std::string samplePath; int mainWindowX = u::gui::centerWindowX(G_MIN_GUI_WIDTH); int mainWindowY = u::gui::centerWindowY(G_MIN_GUI_HEIGHT); int mainWindowW = G_MIN_GUI_WIDTH; 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 browserH = G_DEFAULT_SUBWINDOW_H; int browserPosition; int browserLastValue; std::string browserLastPath; 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 actionEditorZoom = G_DEFAULT_ZOOM_RATIO; int actionEditorSplitH = -1; int actionEditorGridVal = 0; int actionEditorGridOn = false; int actionEditorPianoRollY = -1; int sampleEditorX; int sampleEditorY; 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 midiInputH = G_DEFAULT_SUBWINDOW_H; 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; uint32_t midiInRewind = 0x0; uint32_t midiInStartStop = 0x0; uint32_t midiInActionRec = 0x0; uint32_t midiInInputRec = 0x0; uint32_t midiInMetronome = 0x0; uint32_t midiInVolumeIn = 0x0; uint32_t midiInVolumeOut = 0x0; uint32_t midiInBeatDouble = 0x0; uint32_t midiInBeatHalf = 0x0; #ifdef WITH_VST int pluginChooserX; int pluginChooserY; int pluginChooserW = G_DEFAULT_SUBWINDOW_W; int pluginChooserH = G_DEFAULT_SUBWINDOW_H; int pluginSortMethod = 0; #endif KeyBindings keyBindings = { ' ', // KEY_BIND_PLAY FL_BackSpace, // KEY_BIND_REWIND FL_Enter, // KEY_BIND_RECORD_ACTIONS FL_End, // KEY_BIND_RECORD_INPUT FL_Escape // KEY_BIND_EXIT }; }; Conf(); bool read(); bool write() const; Data data; private: /* createConfigFolder Creates local folder where to put the configuration file. Path differs from OS to OS. */ bool createConfigFolder() const; void sanitize(); std::string m_confFilePath; std::string m_confDirPath; }; } // namespace giada::m #endif giada-0.22.0/src/core/const.h000066400000000000000000000565361425106661500157030ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_CONST_H #define G_CONST_H #include #include /* -- debug ----------------------------------------------------------------- */ #ifndef NDEBUG #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) #endif /* -- environment ----------------------------------------------------------- */ #if defined(_WIN32) #define G_OS_WINDOWS #elif defined(__APPLE__) #define G_OS_MAC #elif defined(__linux__) #define G_OS_LINUX #elif defined(__FreeBSD__) #define G_OS_FREEBSD #endif #ifndef BUILD_DATE #define BUILD_DATE __DATE__ #endif /* -- version --------------------------------------------------------------- */ constexpr auto G_APP_NAME = "Giada"; constexpr auto G_VERSION_STR = "0.22.0"; constexpr int G_VERSION_MAJOR = 0; constexpr int G_VERSION_MINOR = 22; constexpr int G_VERSION_PATCH = 0; constexpr auto CONF_FILENAME = "giada.conf"; /* -- 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 int G_GUI_FPS = 30; constexpr float G_GUI_REFRESH_RATE = 1 / static_cast(G_GUI_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_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_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_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_DISPATCHER_EVENTS = 32; constexpr int G_MAX_SEQUENCER_EVENTS = 128; // Per block /* -- kernel audio ---------------------------------------------------------- */ 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 = 1; constexpr int G_MIDI_API_ALSA = 2; constexpr int G_MIDI_API_MM = 3; constexpr int G_MIDI_API_CORE = 4; /* -- default system -------------------------------------------------------- */ #if defined(G_OS_LINUX) constexpr int G_DEFAULT_SOUNDSYS = G_SYS_API_NONE; #elif defined(G_OS_FREEBSD) constexpr int G_DEFAULT_SOUNDSYS = G_SYS_API_PULSE; #elif defined(G_OS_WINDOWS) constexpr int G_DEFAULT_SOUNDSYS = G_SYS_API_DS; #elif defined(G_OS_MAC) constexpr int G_DEFAULT_SOUNDSYS = G_SYS_API_CORE; #endif 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; 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_COLUMN_WIDTH = 380; constexpr auto G_DEFAULT_PATCH_NAME = "(default patch)"; 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 /* -- responses and return codes -------------------------------------------- */ constexpr int G_RES_ERR_PROCESSING = -6; constexpr int G_RES_ERR_WRONG_DATA = -5; constexpr int G_RES_ERR_NO_DATA = -4; constexpr int G_RES_ERR_PATH_TOO_LONG = -3; constexpr int G_RES_ERR_IO = -2; constexpr int G_RES_ERR_MEMORY = -1; constexpr int G_RES_ERR = 0; constexpr int G_RES_OK = 1; /* -- log modes ------------------------------------------------------------- */ constexpr int LOG_MODE_MUTE = 0; constexpr int LOG_MODE_STDOUT = 1; constexpr int LOG_MODE_FILE = 2; /* -- unique IDs of mainWin's subwindows ------------------------------------ */ /* -- wid > 0 are reserved by gg_keyboard ----------------------------------- */ constexpr int WID_BEATS = -1; constexpr int WID_BPM = -2; constexpr int WID_ABOUT = -3; constexpr int WID_FILE_BROWSER = -4; constexpr int WID_CONFIG = -5; constexpr int WID_FX_LIST = -6; constexpr int WID_ACTION_EDITOR = -7; constexpr int WID_SAMPLE_EDITOR = -8; constexpr int WID_FX = -9; constexpr int WID_KEY_GRABBER = -10; constexpr int WID_SAMPLE_NAME = -11; constexpr int WID_FX_CHOOSER = -12; constexpr int WID_MIDI_INPUT = -13; constexpr int WID_MIDI_OUTPUT = -14; constexpr int WID_MISSING_ASSETS = -15; /* -- File signals ---------------------------------------------------------- */ constexpr int G_FILE_NOT_SPECIFIED = -3; constexpr int G_FILE_UNSUPPORTED = -2; constexpr int G_FILE_UNREADABLE = -1; constexpr int G_FILE_INVALID = 0; constexpr int G_FILE_OK = 1; /* -- 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_REWIND = 3; constexpr int G_MIDI_IN_START_STOP = 4; constexpr int G_MIDI_IN_ACTION_REC = 5; constexpr int G_MIDI_IN_INPUT_REC = 6; constexpr int G_MIDI_IN_METRONOME = 7; constexpr int G_MIDI_IN_VOLUME_IN = 8; constexpr int G_MIDI_IN_VOLUME_OUT = 9; constexpr int G_MIDI_IN_BEAT_DOUBLE = 10; constexpr int G_MIDI_IN_BEAT_HALF = 11; constexpr int G_MIDI_IN_KEYPRESS = 12; constexpr int G_MIDI_IN_KEYREL = 13; constexpr int G_MIDI_IN_KILL = 14; constexpr int G_MIDI_IN_ARM = 15; constexpr int G_MIDI_IN_MUTE = 16; constexpr int G_MIDI_IN_SOLO = 17; constexpr int G_MIDI_IN_VOLUME = 18; 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; /* -- MIDI signals ------------------------------------------------------------- Channel voices messages - controller (0xB0) is a special subset of this family: it drives knobs, volume, faders and such. */ constexpr uint32_t G_MIDI_CONTROLLER = static_cast(0xB0 << 24); constexpr uint32_t G_MIDI_ALL_NOTES_OFF = (G_MIDI_CONTROLLER) | (0x7B << 16); /* system common / real-time messages. Single bytes */ constexpr int MIDI_SYSEX = 0xF0; constexpr int MIDI_MTC_QUARTER = 0xF1; constexpr int MIDI_POSITION_PTR = 0xF2; constexpr int MIDI_CLOCK = 0xF8; constexpr int MIDI_START = 0xFA; constexpr int MIDI_CONTINUE = 0xFB; constexpr int MIDI_STOP = 0xFC; constexpr int MIDI_EOX = 0xF7; // end of sysex /* midi sync constants */ constexpr int G_MIDI_SYNC_NONE = 0; constexpr int G_MIDI_SYNC_CLOCK_M = 1; // master constexpr int G_MIDI_SYNC_CLOCK_S = 2; // slave constexpr int G_MIDI_SYNC_MTC_M = 3; // master constexpr int G_MIDI_SYNC_MTC_S = 4; // slave /* JSON patch keys */ constexpr auto PATCH_KEY_HEADER = "header"; constexpr auto PATCH_KEY_VERSION_MAJOR = "version_major"; constexpr auto PATCH_KEY_VERSION_MINOR = "version_minor"; constexpr auto PATCH_KEY_VERSION_PATCH = "version_patch"; constexpr auto PATCH_KEY_NAME = "name"; constexpr auto PATCH_KEY_BPM = "bpm"; constexpr auto PATCH_KEY_BARS = "bars"; constexpr auto PATCH_KEY_BEATS = "beats"; constexpr auto PATCH_KEY_QUANTIZE = "quantize"; constexpr auto PATCH_KEY_MASTER_VOL_IN = "master_vol_in"; constexpr auto PATCH_KEY_MASTER_VOL_OUT = "master_vol_out"; constexpr auto PATCH_KEY_METRONOME = "metronome"; constexpr auto PATCH_KEY_LAST_TAKE_ID = "last_take_id"; constexpr auto PATCH_KEY_SAMPLERATE = "samplerate"; constexpr auto PATCH_KEY_COLUMNS = "columns"; constexpr auto PATCH_KEY_PLUGINS = "plugins"; constexpr auto PATCH_KEY_MASTER_OUT_PLUGINS = "master_out_plugins"; constexpr auto PATCH_KEY_MASTER_IN_PLUGINS = "master_in_plugins"; constexpr auto PATCH_KEY_CHANNELS = "channels"; constexpr auto PATCH_KEY_CHANNEL_TYPE = "type"; constexpr auto PATCH_KEY_CHANNEL_ID = "id"; constexpr auto PATCH_KEY_CHANNEL_SIZE = "size"; constexpr auto PATCH_KEY_CHANNEL_NAME = "name"; constexpr auto PATCH_KEY_CHANNEL_COLUMN = "column"; constexpr auto PATCH_KEY_CHANNEL_MUTE = "mute"; constexpr auto PATCH_KEY_CHANNEL_SOLO = "solo"; constexpr auto PATCH_KEY_CHANNEL_VOLUME = "volume"; constexpr auto PATCH_KEY_CHANNEL_PAN = "pan"; constexpr auto PATCH_KEY_CHANNEL_MIDI_IN = "midi_in"; constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_VELO_AS_VOL = "midi_in_velo_as_vol"; constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS = "midi_in_keypress"; constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_KEYREL = "midi_in_keyrel"; constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_KILL = "midi_in_kill"; constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_ARM = "midi_in_arm"; constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_VOLUME = "midi_in_volume"; constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_MUTE = "midi_in_mute"; constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_FILTER = "midi_in_filter"; constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_SOLO = "midi_in_solo"; constexpr auto PATCH_KEY_CHANNEL_MIDI_OUT_L = "midi_out_l"; constexpr auto PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING = "midi_out_l_playing"; constexpr auto PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE = "midi_out_l_mute"; constexpr auto PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO = "midi_out_l_solo"; constexpr auto PATCH_KEY_CHANNEL_WAVE_ID = "wave_id"; constexpr auto PATCH_KEY_CHANNEL_KEY = "key"; constexpr auto PATCH_KEY_CHANNEL_MODE = "mode"; constexpr auto PATCH_KEY_CHANNEL_BEGIN = "begin"; constexpr auto PATCH_KEY_CHANNEL_END = "end"; constexpr auto PATCH_KEY_CHANNEL_SHIFT = "shift"; constexpr auto PATCH_KEY_CHANNEL_HAS_ACTIONS = "has_actions"; constexpr auto PATCH_KEY_CHANNEL_READ_ACTIONS = "read_actions"; constexpr auto PATCH_KEY_CHANNEL_PITCH = "pitch"; constexpr auto PATCH_KEY_CHANNEL_INPUT_MONITOR = "input_monitor"; constexpr auto PATCH_KEY_CHANNEL_OVERDUB_PROTECTION = "overdub_protection"; constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS = "midi_in_read_actions"; constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_PITCH = "midi_in_pitch"; constexpr auto PATCH_KEY_CHANNEL_MIDI_OUT = "midi_out"; constexpr auto PATCH_KEY_CHANNEL_MIDI_OUT_CHAN = "midi_out_chan"; constexpr auto PATCH_KEY_CHANNEL_PLUGINS = "plugins"; constexpr auto PATCH_KEY_CHANNEL_PLUGIN_ID = "plugin_id"; constexpr auto PATCH_KEY_CHANNEL_ARMED = "armed"; constexpr auto PATCH_KEY_WAVES = "waves"; constexpr auto PATCH_KEY_WAVE_ID = "id"; constexpr auto PATCH_KEY_WAVE_PATH = "path"; constexpr auto PATCH_KEY_ACTIONS = "actions"; constexpr auto PATCH_KEY_ACTION_TYPE = "type"; constexpr auto PATCH_KEY_ACTION_FRAME = "frame"; constexpr auto PATCH_KEY_ACTION_F_VALUE = "f_value"; constexpr auto PATCH_KEY_ACTION_I_VALUE = "i_value"; constexpr auto PATCH_KEY_PLUGIN_ID = "id"; constexpr auto PATCH_KEY_PLUGIN_PATH = "path"; constexpr auto PATCH_KEY_PLUGIN_BYPASS = "bypass"; constexpr auto PATCH_KEY_PLUGIN_PARAMS = "params"; constexpr auto PATCH_KEY_PLUGIN_STATE = "state"; constexpr auto PATCH_KEY_PLUGIN_MIDI_IN_PARAMS = "midi_in_params"; constexpr auto PATCH_KEY_COLUMN_ID = "id"; constexpr auto PATCH_KEY_COLUMN_WIDTH = "width"; constexpr auto PATCH_KEY_COLUMN_CHANNELS = "channels"; constexpr auto G_PATCH_KEY_ACTION_ID = "id"; 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_LANGMAP = "langmap"; 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_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_BUFFER_SIZE = "buffer_size"; constexpr auto CONF_KEY_DELAY_COMPENSATION = "delay_compensation"; constexpr auto CONF_KEY_LIMIT_OUTPUT = "limit_output"; constexpr auto CONF_KEY_RESAMPLE_QUALITY = "resample_quality"; constexpr auto CONF_KEY_MIDI_SYSTEM = "midi_system"; constexpr auto CONF_KEY_MIDI_PORT_OUT = "midi_port_out"; constexpr auto CONF_KEY_MIDI_PORT_IN = "midi_port_in"; constexpr auto CONF_KEY_MIDIMAP_PATH = "midimap_path"; constexpr auto CONF_KEY_LAST_MIDIMAP = "last_midimap"; constexpr auto CONF_KEY_MIDI_SYNC = "midi_sync"; constexpr auto CONF_KEY_MIDI_TC_FPS = "midi_tc_fps"; constexpr auto CONF_KEY_MIDI_IN = "midi_in"; constexpr auto CONF_KEY_MIDI_IN_FILTER = "midi_in_filter"; constexpr auto CONF_KEY_MIDI_IN_REWIND = "midi_in_rewind"; constexpr auto CONF_KEY_MIDI_IN_START_STOP = "midi_in_start_stop"; constexpr auto CONF_KEY_MIDI_IN_ACTION_REC = "midi_in_action_rec"; constexpr auto CONF_KEY_MIDI_IN_INPUT_REC = "midi_in_input_rec"; constexpr auto CONF_KEY_MIDI_IN_METRONOME = "midi_in_metronome"; constexpr auto CONF_KEY_MIDI_IN_VOLUME_IN = "midi_in_volume_in"; constexpr auto CONF_KEY_MIDI_IN_VOLUME_OUT = "midi_in_volume_out"; constexpr auto CONF_KEY_MIDI_IN_BEAT_DOUBLE = "midi_in_beat_doble"; constexpr auto CONF_KEY_MIDI_IN_BEAT_HALF = "midi_in_beat_half"; constexpr auto CONF_KEY_CHANS_STOP_ON_SEQ_HALT = "chans_stop_on_seq_halt"; constexpr auto CONF_KEY_TREAT_RECS_AS_LOOPS = "treat_recs_as_loops"; constexpr auto CONF_KEY_INPUT_MONITOR_DEFAULT_ON = "input_monitor_default_on"; constexpr auto CONF_KEY_OVERDUB_PROTECTION_DEFAULT_ON = "overdub_protection_default_on"; constexpr auto CONF_KEY_PLUGINS_PATH = "plugins_path"; constexpr auto CONF_KEY_PATCHES_PATH = "patches_path"; constexpr auto CONF_KEY_SAMPLES_PATH = "samples_path"; constexpr auto CONF_KEY_MAIN_WINDOW_X = "main_window_x"; constexpr auto CONF_KEY_MAIN_WINDOW_Y = "main_window_y"; constexpr auto CONF_KEY_MAIN_WINDOW_W = "main_window_w"; constexpr auto CONF_KEY_MAIN_WINDOW_H = "main_window_h"; constexpr auto CONF_KEY_BROWSER_X = "browser_x"; constexpr auto CONF_KEY_BROWSER_Y = "browser_y"; constexpr auto CONF_KEY_BROWSER_W = "browser_w"; constexpr auto CONF_KEY_BROWSER_H = "browser_h"; constexpr auto CONF_KEY_BROWSER_POSITION = "browser_position"; constexpr auto CONF_KEY_BROWSER_LAST_PATH = "browser_last_path"; constexpr auto CONF_KEY_BROWSER_LAST_VALUE = "browser_last_value"; constexpr auto CONF_KEY_ACTION_EDITOR_X = "action_editor_x"; constexpr auto CONF_KEY_ACTION_EDITOR_Y = "action_editor_y"; constexpr auto CONF_KEY_ACTION_EDITOR_W = "action_editor_w"; constexpr auto CONF_KEY_ACTION_EDITOR_H = "action_editor_h"; constexpr auto CONF_KEY_ACTION_EDITOR_ZOOM = "action_editor_zoom"; constexpr auto CONF_KEY_ACTION_EDITOR_SPLIT_H = "action_editor_split_h"; constexpr auto CONF_KEY_ACTION_EDITOR_GRID_VAL = "action_editor_grid_val"; constexpr auto CONF_KEY_ACTION_EDITOR_GRID_ON = "action_editor_grid_on"; constexpr auto CONF_KEY_SAMPLE_EDITOR_X = "sample_editor_x"; constexpr auto CONF_KEY_SAMPLE_EDITOR_Y = "sample_editor_y"; constexpr auto CONF_KEY_SAMPLE_EDITOR_W = "sample_editor_w"; constexpr auto CONF_KEY_SAMPLE_EDITOR_H = "sample_editor_h"; constexpr auto CONF_KEY_SAMPLE_EDITOR_GRID_VAL = "sample_editor_grid_val"; constexpr auto CONF_KEY_SAMPLE_EDITOR_GRID_ON = "sample_editor_grid_on"; constexpr auto CONF_KEY_ACTION_EDITOR_PIANO_ROLL_Y = "piano_roll_y"; constexpr auto CONF_KEY_PLUGIN_LIST_X = "plugin_list_x"; constexpr auto CONF_KEY_PLUGIN_LIST_Y = "plugin_list_y"; constexpr auto CONF_KEY_PLUGIN_CHOOSER_X = "plugin_chooser_x"; constexpr auto CONF_KEY_PLUGIN_CHOOSER_Y = "plugin_chooser_y"; constexpr auto CONF_KEY_PLUGIN_CHOOSER_W = "plugin_chooser_w"; constexpr auto CONF_KEY_PLUGIN_CHOOSER_H = "plugin_chooser_h"; constexpr auto CONF_KEY_MIDI_INPUT_X = "midi_input_x"; constexpr auto CONF_KEY_MIDI_INPUT_Y = "midi_input_y"; constexpr auto CONF_KEY_MIDI_INPUT_W = "midi_input_w"; constexpr auto CONF_KEY_MIDI_INPUT_H = "midi_input_h"; 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"; constexpr auto CONF_KEY_BIND_PLAY = "key_bind_play"; constexpr auto CONF_KEY_BIND_REWIND = "key_bind_rewind"; constexpr auto CONF_KEY_BIND_RECORD_ACTIONS = "key_bind_record_actions"; constexpr auto CONF_KEY_BIND_RECORD_INPUT = "key_bind_record_input"; constexpr auto CONF_KEY_BIND_EXIT = "key_bind_record_exit"; /* JSON midimaps keys */ constexpr auto MIDIMAP_KEY_BRAND = "brand"; constexpr auto MIDIMAP_KEY_DEVICE = "device"; constexpr auto MIDIMAP_KEY_INIT_COMMANDS = "init_commands"; constexpr auto MIDIMAP_KEY_MUTE_ON = "mute_on"; constexpr auto MIDIMAP_KEY_MUTE_OFF = "mute_off"; constexpr auto MIDIMAP_KEY_SOLO_ON = "solo_on"; constexpr auto MIDIMAP_KEY_SOLO_OFF = "solo_off"; constexpr auto MIDIMAP_KEY_WAITING = "waiting"; constexpr auto MIDIMAP_KEY_PLAYING = "playing"; constexpr auto MIDIMAP_KEY_PLAYING_INAUDIBLE = "playing_inaudible"; constexpr auto MIDIMAP_KEY_STOPPING = "stopping"; constexpr auto MIDIMAP_KEY_STOPPED = "stopped"; constexpr auto MIDIMAP_KEY_CHANNEL = "channel"; constexpr auto MIDIMAP_KEY_MESSAGE = "message"; #endif giada-0.22.0/src/core/engine.cpp000066400000000000000000000334371425106661500163500ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/engine.h" #include "core/model/model.h" #include "core/model/storage.h" #include "utils/fs.h" #include "utils/log.h" #include namespace giada::m { bool LoadState::isGood() const { return patch == G_FILE_OK && missingWaves.empty() && missingPlugins.empty(); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ Engine::Engine() : midiMapper(kernelMidi) , channelManager(conf.data, model) , midiDispatcher(model) , actionRecorder(model) , synchronizer(conf.data, kernelMidi) , sequencer(model, synchronizer, jackTransport) , mixer(model) , mixerHandler(model, mixer) , recorder(model, sequencer, mixerHandler) #ifdef WITH_VST , pluginHost(model) #endif { kernelAudio.onAudioCallback = [this](KernelAudio::CallbackInfo info) { return audioCallback(info); }; kernelMidi.onMidiReceived = [this](uint32_t msg) { midiDispatcher.dispatch(msg); }; #ifdef WITH_AUDIO_JACK synchronizer.onJackRewind = [this]() { eventDispatcher.pumpMidiEvent({EventDispatcher::EventType::SEQUENCER_REWIND_JACK}); }; synchronizer.onJackChangeBpm = [this](float bpm) { eventDispatcher.pumpMidiEvent({EventDispatcher::EventType::SEQUENCER_BPM_JACK, 0, 0, bpm}); }; synchronizer.onJackStart = [this]() { eventDispatcher.pumpMidiEvent({EventDispatcher::EventType::SEQUENCER_START_JACK}); }; synchronizer.onJackStop = [this]() { eventDispatcher.pumpMidiEvent({EventDispatcher::EventType::SEQUENCER_STOP_JACK}); }; #endif eventDispatcher.onMidiLearn = [this](const MidiEvent& e) { midiDispatcher.learn(e); }; eventDispatcher.onMidiProcess = [this](const MidiEvent& e) { midiDispatcher.process(e); }; eventDispatcher.onProcessChannels = [this](const EventDispatcher::EventBuffer& eb) { for (Channel& ch : model.get().channels) ch.react(eb); model.swap(model::SwapType::SOFT); }; eventDispatcher.onProcessSequencer = [this](const EventDispatcher::EventBuffer& eb) { sequencer.react(eb, kernelAudio.getSampleRate()); }; eventDispatcher.onMixerSignalCallback = [this]() { recorder.startInputRecOnCallback(); }; eventDispatcher.onMixerEndOfRecCallback = [this]() { if (recorder.isRecordingInput()) recorder.stopInputRec(conf.data.inputRecMode, kernelAudio.getSampleRate()); }; midiDispatcher.onDispatch = [this](EventDispatcher::EventType event, Action action) { /* Notify Event Dispatcher when a MIDI signal is received. */ eventDispatcher.pumpMidiEvent({event, 0, 0, action}); }; midiDispatcher.onEventReceived = [this]() { recorder.startActionRecOnCallback(); }; mixer.onSignalTresholdReached = [this]() { /* Invokes the signal callback. This is done by pumping a MIXER_SIGNAL_CALLBACK 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. */ eventDispatcher.pumpUIevent({EventDispatcher::EventType::MIXER_SIGNAL_CALLBACK}); }; mixer.onEndOfRecording = [this]() { /* Same rationale as above, for the end-of-recording callback. */ eventDispatcher.pumpUIevent({EventDispatcher::EventType::MIXER_END_OF_REC_CALLBACK}); }; mixerHandler.onChannelsAltered = [this]() { if (!recorder.canEnableFreeInputRec()) conf.data.inputRecMode = InputRecMode::RIGID; }; mixerHandler.onChannelRecorded = [this](Frame recordedFrames) { std::string filename = "TAKE-" + std::to_string(patch.data.lastTakeId++) + ".wav"; return waveManager.createEmpty(recordedFrames, G_MAX_IO_CHANS, kernelAudio.getSampleRate(), filename); }; sequencer.onAboutStart = [this](SeqStatus status) { /* TODO move this logic to Recorder */ if (status == SeqStatus::WAITING) recorder.stopActionRec(actionRecorder); conf.data.recTriggerMode = RecTriggerMode::NORMAL; }; sequencer.onAboutStop = [this]() { /* If recordings (both input and action) are active deactivate them, but store the takes. RecManager takes care of it. */ /* TODO move this logic to Recorder */ if (recorder.isRecordingAction()) recorder.stopActionRec(actionRecorder); else if (recorder.isRecordingInput()) recorder.stopInputRec(conf.data.inputRecMode, kernelAudio.getSampleRate()); }; sequencer.onBpmChange = [this](float oldVal, float newVal, int quantizerStep) { actionRecorder.updateBpm(oldVal / newVal, quantizerStep); }; } /* -------------------------------------------------------------------------- */ void Engine::updateMixerModel() { model.get().mixer.limitOutput = conf.data.limitOutput; model.get().mixer.allowsOverdub = conf.data.inputRecMode == InputRecMode::RIGID; model.get().mixer.maxFramesToRec = conf.data.inputRecMode == InputRecMode::FREE ? sequencer.getMaxFramesInLoop(kernelAudio.getSampleRate()) : sequencer.getFramesInLoop(); model.get().mixer.recTriggerLevel = conf.data.recTriggerLevel; model.swap(model::SwapType::NONE); } /* -------------------------------------------------------------------------- */ void Engine::init() { if (!conf.read()) u::log::print("[Engine::init] Can't read configuration file! Using default values\n"); model::load(conf.data); if (!u::log::init(conf.data.logMode)) u::log::print("[Engine::init] log init failed! Using default stdout\n"); init::printBuildInfo(); midiMapper.init(); if (midiMapper.read(conf.data.midiMapPath) != G_FILE_OK) u::log::print("[Engine::init] MIDI map read failed!\n"); /* Initialize KernelAudio. If fails, interrupt the Engine initialization: Giada can't work without a functional KernelAudio. */ kernelAudio.openDevice(conf.data); if (!kernelAudio.isReady()) return; #ifdef WITH_AUDIO_JACK if (kernelAudio.getAPI() == G_SYS_API_JACK) jackTransport.setHandle(kernelAudio.getJackHandle()); #endif mixerHandler.reset(sequencer.getMaxFramesInLoop(kernelAudio.getSampleRate()), kernelAudio.getBufferSize(), channelManager); sequencer.reset(kernelAudio.getSampleRate()); #ifdef WITH_VST pluginHost.reset(kernelAudio.getBufferSize()); pluginManager.reset(static_cast(conf.data.pluginSortMethod)); #endif mixer.enable(); kernelAudio.startStream(); kernelMidi.openOutDevice(conf.data.midiSystem, conf.data.midiPortOut); kernelMidi.openInDevice(conf.data.midiSystem, conf.data.midiPortIn); kernelMidi.logPorts(); midiMapper.sendInitMessages(midiMapper.currentMap); eventDispatcher.start(); updateMixerModel(); } /* -------------------------------------------------------------------------- */ void Engine::reset() { /* Managers first, due to the internal ID numbering. */ channelManager.reset(); waveManager.reset(); #ifdef WITH_VST pluginManager.reset(static_cast(conf.data.pluginSortMethod)); #endif /* Then all other components. */ model.reset(); mixerHandler.reset(sequencer.getMaxFramesInLoop(kernelAudio.getSampleRate()), kernelAudio.getBufferSize(), channelManager); synchronizer.reset(); sequencer.reset(kernelAudio.getSampleRate()); actionRecorder.reset(); #ifdef WITH_VST pluginHost.reset(kernelAudio.getBufferSize()); #endif } /* -------------------------------------------------------------------------- */ void Engine::shutdown() { if (kernelAudio.isReady()) { kernelAudio.closeDevice(); u::log::print("[Engine::shutdown] KernelAudio closed\n"); mixer.disable(); u::log::print("[Engine::shutdown] Mixer closed\n"); } model::store(conf.data); if (!conf.write()) u::log::print("[Engine::shutdown] error while saving configuration file!\n"); else u::log::print("[Engine::shutdown] configuration saved\n"); u::log::close(); #ifdef WITH_VST /* Currently the Engine is global/static, and so are all of its sub-components, Model included. Some plug-ins (JUCE-based ones) crash hard on destructor when deleted as a result of returning from main, so it's better to free them all first. TODO - investigate this! */ pluginHost.freeAllPlugins(); #endif } /* -------------------------------------------------------------------------- */ int Engine::audioCallback(KernelAudio::CallbackInfo kernelInfo) { mcl::AudioBuffer out(static_cast(kernelInfo.outBuf), kernelInfo.bufferSize, kernelInfo.channelsOutCount); mcl::AudioBuffer in; if (kernelInfo.channelsInCount > 0) in = mcl::AudioBuffer(static_cast(kernelInfo.inBuf), kernelInfo.bufferSize, kernelInfo.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 (!kernelInfo.ready) return 0; /* Prepare the LayoutLock. From this point on (until out of scope) the Layout is locked for realtime rendering by the audio thread. Rendering functions must access the realtime layout coming from layoutLock.get(). */ const model::LayoutLock layoutLock = model.get_RT(); const model::Layout& layout_RT = layoutLock.get(); /* Mixer disabled, nothing to do here. */ if (!layout_RT.mixer.a_isActive()) return 0; #ifdef WITH_AUDIO_JACK if (kernelInfo.withJack) synchronizer.recvJackSync(jackTransport.getState()); #endif /* If the sequencer is running, advance it first (i.e. parse it for events). Also advance channels (i.e. let them react to sequencer events), only if the layout is not locked: another thread might altering channel's data in the meantime (e.g. Plugins or Waves). */ if (layout_RT.sequencer.isRunning()) { const Frame currentFrame = sequencer.getCurrentFrame(); const Frame bufferSize = in.countFrames(); const Frame quantizerStep = sequencer.getQuantizerStep(); // TODO pass this to sequencer.advance - or better, Advancer class const Range renderRange = {currentFrame, currentFrame + bufferSize}; // TODO pass this to sequencer.advance - or better, Advancer class const Sequencer::EventBuffer& events = sequencer.advance(bufferSize, actionRecorder); sequencer.render(out); if (!layout_RT.locked) mixer.advanceChannels(events, layout_RT, renderRange, quantizerStep); } /* Then render Mixer: render channels, process I/O. */ mixer.render(out, in, layout_RT); return 0; } /* -------------------------------------------------------------------------- */ bool Engine::store(const std::string& projectName, const std::string& projectPath, const std::string& patchPath, std::function progress) { progress(0.0f); if (!u::fs::mkdir(projectPath)) { u::log::print("[Engine::store] Unable to make project directory!\n"); return false; } u::log::print("[Engine::store] Project dir created: %s\n", projectPath); /* Update all existing file paths in Waves, so that they point to the project folder they belong to. */ for (std::unique_ptr& w : model.getAllShared()) { w->setPath(makeUniqueWavePath(projectPath, *w, model.getAllShared())); waveManager.save(*w, w->getPath()); // TODO - error checking } progress(0.3f); /* Write Model into Patch, then into file. */ patch.data.name = projectName; model::store(patch.data); progress(0.6f); if (!patch.write(patchPath)) return false; /* Store the parent folder the project belongs to, in order to reuse it the next time. */ conf.data.patchPath = u::fs::getUpDir(u::fs::getUpDir(patchPath)); u::log::print("[Engine::store] Project patch saved as %s\n", patchPath); progress(1.0f); return true; } /* -------------------------------------------------------------------------- */ LoadState Engine::load(const std::string& projectPath, const std::string& patchPath, std::function progress) { u::log::print("[Engine::load] Load project from %s\n", projectPath); progress(0.0f); patch.reset(); if (int res = patch.read(patchPath, projectPath); res != G_FILE_OK) return {res}; progress(0.3f); /* Then suspend Mixer, reset and fill the model. */ mixer.disable(); reset(); LoadState state = m::model::load(patch.data); progress(0.6f); /* Prepare the engine. Recorder has to recompute the actions positions if the current samplerate != patch samplerate. Clock needs to update frames in sequencer. */ mixerHandler.updateSoloCount(); actionRecorder.updateSamplerate(kernelAudio.getSampleRate(), patch.data.samplerate); sequencer.recomputeFrames(kernelAudio.getSampleRate()); mixer.allocRecBuffer(sequencer.getMaxFramesInLoop(kernelAudio.getSampleRate())); progress(0.9f); /* Store the parent folder the project belongs to, in order to reuse it the next time. */ conf.data.patchPath = u::fs::getUpDir(projectPath); /* Mixer is ready to go back online. */ mixer.enable(); progress(1.0f); state.patch = G_FILE_OK; return state; } } // namespace giada::m giada-0.22.0/src/core/engine.h000066400000000000000000000100361425106661500160030ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_ENGINE_H #define G_ENGINE_H #include "core/actions/actionRecorder.h" #include "core/actions/actions.h" #include "core/channels/channelManager.h" #include "core/conf.h" #include "core/eventDispatcher.h" #include "core/init.h" #include "core/jackTransport.h" #include "core/kernelAudio.h" #include "core/kernelMidi.h" #include "core/midiDispatcher.h" #include "core/midiMapper.h" #include "core/mixer.h" #include "core/mixerHandler.h" #include "core/model/model.h" #include "core/patch.h" #include "core/plugins/pluginHost.h" #include "core/plugins/pluginManager.h" #include "core/recorder.h" #include "core/sequencer.h" #include "core/synchronizer.h" #include "core/waveManager.h" namespace giada::m { struct LoadState { bool isGood() const; int patch = G_FILE_OK; std::vector missingWaves = {}; std::vector missingPlugins = {}; }; class Engine final { public: /* Engine() Prepares all sub-components by constructing them and setting up the required callback for inter-component communication. It doesn't start the engine yet. */ Engine(); /* store Saves the current state to a Patch, then saves it to file. Returns true on success. */ bool store(const std::string& projectName, const std::string& projectPath, const std::string& patchPath, std::function progress); /* load Reads a Patch from file and then de-serialize its content into the model. Returns a LoadState object. */ LoadState load(const std::string& projectPath, const std::string& patchPath, std::function progress); /* updateMixerModel Updates some values in model::Mixer data struct needed by m::Mixer for the audio rendering. Call this whenever the audio configuration changes. */ void updateMixerModel(); /* init Initializes all sub-components. If KernelAudio fails to start, the process interrupts and Giada is put in an invalid state. */ void init(); /* reset Resets all sub-components to the initial state. Useful when Giada needs to be brought back to the startup state. */ void reset(); /* shutdown Closes the current audio device. */ void shutdown(); model::Model model; Conf conf; Patch patch; KernelAudio kernelAudio; KernelMidi kernelMidi; JackTransport jackTransport; WaveManager waveManager; EventDispatcher eventDispatcher; MidiMapper midiMapper; ChannelManager channelManager; MidiDispatcher midiDispatcher; ActionRecorder actionRecorder; Synchronizer synchronizer; Sequencer sequencer; Mixer mixer; MixerHandler mixerHandler; Recorder recorder; #ifdef WITH_VST PluginHost pluginHost; PluginManager pluginManager; #endif private: int audioCallback(KernelAudio::CallbackInfo); }; } // namespace giada::m #endif giada-0.22.0/src/core/eventDispatcher.cpp000066400000000000000000000060421425106661500202230ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * 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 * . * * -------------------------------------------------------------------------- */ #include "core/eventDispatcher.h" #include "core/const.h" #include namespace giada::m { EventDispatcher::EventDispatcher() : onMidiLearn(nullptr) , onMidiProcess(nullptr) , onProcessChannels(nullptr) , onProcessSequencer(nullptr) , onMixerSignalCallback(nullptr) , onMixerEndOfRecCallback(nullptr) { } /* -------------------------------------------------------------------------- */ void EventDispatcher::start() { m_worker.start([this]() { process(); }, /*sleep=*/G_EVENT_DISPATCHER_RATE_MS); } /* -------------------------------------------------------------------------- */ void EventDispatcher::pumpUIevent(Event e) { UIevents.push(e); } void EventDispatcher::pumpMidiEvent(Event e) { MidiEvents.push(e); } /* -------------------------------------------------------------------------- */ void EventDispatcher::processFunctions() { assert(onMidiLearn != nullptr); assert(onMidiProcess != nullptr); assert(onMixerSignalCallback != nullptr); assert(onMixerEndOfRecCallback != nullptr); for (const Event& e : m_eventBuffer) { switch (e.type) { case EventType::MIDI_DISPATCHER_LEARN: onMidiLearn(std::get(e.data).event); break; case EventType::MIDI_DISPATCHER_PROCESS: onMidiProcess(std::get(e.data).event); break; case EventType::MIXER_SIGNAL_CALLBACK: onMixerSignalCallback(); break; case EventType::MIXER_END_OF_REC_CALLBACK: onMixerEndOfRecCallback(); break; default: break; } } } /* -------------------------------------------------------------------------- */ void EventDispatcher::process() { assert(onProcessChannels != nullptr); assert(onProcessSequencer != nullptr); m_eventBuffer.clear(); Event e; while (UIevents.pop(e)) m_eventBuffer.push_back(e); while (MidiEvents.pop(e)) m_eventBuffer.push_back(e); if (m_eventBuffer.size() == 0) return; processFunctions(); onProcessChannels(m_eventBuffer); onProcessSequencer(m_eventBuffer); } } // namespace giada::mgiada-0.22.0/src/core/eventDispatcher.h000066400000000000000000000076561425106661500177040ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * 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 * . * * -------------------------------------------------------------------------- */ #ifndef G_EVENT_DISPATCHER_H #define G_EVENT_DISPATCHER_H #include "core/const.h" #include "core/queue.h" #include "core/ringBuffer.h" #include "core/types.h" #include "core/worker.h" #include "src/core/actions/action.h" #include #include #include #include /* 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 { class EventDispatcher { public: enum class EventType { KEY_PRESS, KEY_RELEASE, KEY_KILL, SEQUENCER_START, SEQUENCER_STOP, SEQUENCER_REWIND, #ifdef WITH_AUDIO_JACK SEQUENCER_START_JACK, SEQUENCER_STOP_JACK, SEQUENCER_REWIND_JACK, SEQUENCER_BPM_JACK, #endif MIDI, MIDI_DISPATCHER_LEARN, MIDI_DISPATCHER_PROCESS, MIXER_SIGNAL_CALLBACK, MIXER_END_OF_REC_CALLBACK, CHANNEL_TOGGLE_READ_ACTIONS, CHANNEL_KILL_READ_ACTIONS, CHANNEL_TOGGLE_ARM, CHANNEL_MUTE, CHANNEL_SOLO, CHANNEL_VOLUME, CHANNEL_PITCH, CHANNEL_PAN }; struct Event { using EventData = std::variant; 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; EventDispatcher(); /* start Starts the internal worker on a separate thread. Call this on startup. */ void start(); void pumpUIevent(Event e); void pumpMidiEvent(Event e); /* Event queues Collect events coming from the UI or MIDI devices. Our poor man's 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! */ /*TODO - make them private*/ Queue UIevents; Queue MidiEvents; /* on[...] Callbacks fired when something happens in the Event Dispatcher. */ std::function onMidiLearn; std::function onMidiProcess; std::function onProcessChannels; std::function onProcessSequencer; std::function onMixerSignalCallback; std::function onMixerEndOfRecCallback; private: void processFunctions(); void process(); /* m_worker A separate thread responsible for the event processing. */ Worker m_worker; /* m_eventBuffer Buffer of events sent to channels for event parsing. This is filled with Events coming from the two event queues.*/ EventBuffer m_eventBuffer; }; } // namespace giada::m #endif giada-0.22.0/src/core/graphics.cpp000066400000000000000000002106211425106661500166730ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "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", " ...+++@@@@++... ", " .+@@@@@@@@@@@@@@@@@@@@. ", " .+@@@@@@@@@@@@@@@@@@@@@@@@@@+. ", " .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ", " .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ", " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ", " +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ", " .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+ ", " .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ", " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ", " @@@@@@@@@@@@@@@@@@++$*--;;;;;;;;-=&@+@@@@@@@@@@@@@@@@@@+ ", " .@@@@@@@@@@@@@@@@+#&=;;;;;;;;;;;;;;;;;-*%++@@@@@@@@@@@@@@@+ ", " .@@@@@@@@@@@@@@@@$=;;;;;;;;;;;;;;;;;;;;;;;;&#+@@@@@@@@@@@@@@@ ", " .@@@@@@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*@@@@@@@@@@@@@@@@ ", " @@@@@@@@@@@@@@#-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*+@@@@@@@@@@@@@+ ", " @@@@@@@@@@@@@+*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;$+@@@@@@@@@@@@+ ", " @@@@@@@@@@@@@#-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;&@@@@@@@@@@@@@. ", " .@@@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-+@@@@@@@@@@@@ ", " .@@@@@@@@@@@+%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-@@@@@@@@@@@@@ ", " @@@@@@@@@@@+&;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;#@@@@@@@@@@@. ", " +@@@@@@@@@@@%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@@@@@@@@@@@@ ", " @@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-+@@@@@@@@@@+ ", " @@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;=@@@@@@@@@@@. ", " .@@@@@@@@@@+-;;;;;;;;;;;;;;;;;;;;;=*&%%%%&*-;;;;;;;;;;;;;;;;;;;;;%+@@@@@@@@@@ ", " +@@@@@@@@@@*;;;;;;;;;;;;;;;;;;;-%++@@@@@@@++@*-;;;;;;;;;;;;;;;;;;;#@@@@@@@@@@. ", " .@@@@@@@@@@#;;;;;;;;;;;;;;;;;;=@@@@@@@@@@@@@@@@@$;;;;;;;;;;;;;;;;;;=+@@@@@@@@@@ ", " +@@@@@@@@@@-;;;;;;;;;;;;;;;;-$+@@@@@@@@@@@@@@@@@@@&;;;;;;;;;;;;;;;;;%@@@@@@@@@@. %*------=*%. $%%%%%%% $%%%%%%%%. #%%%%%%%%%%%%%%$@ %%%%%%%%% ", " @@@@@@@@@@%;;;;;;;;;;;;;;;;*@@@@@@@@@@@@@@@@@@@@@@+$;;;;;;;;;;;;;;;;-+@@@@@@@@@+ #=;;;;;;;;;;;;;=$ .;;;;;;;;& &;;;;;;;;;; ;;;;;;;;;;;;;;;;;;-&# -;;;;;;;;;= ", " .@@@@@@@@@@;;;;;;;;;;;;;;;;*+@@@@@@@@@@@@@@@@@@@@@@@@@;;;;;;;;;;;;;;;;%@@@@@@@@@@ .=;;;;;;;;;;;;;;;;;;# +;;;;;;;;* ;;;;;;;;;;;& .;;;;;;;;;;;;;;;;;;;;;;@ .;;;;;;;;;;;. ", " +@@@@@@@@@%;;;;;;;;;;;;;;;*@@@@@@@@@@@@@@@@@@@@@@@@@@@@;;;;;;;;;;;;;;;;@@@@@@@@@@. @;;;;;;;;;;;;;;;;;;;;;* +;;;;;;;;* +;;;;;;;;;;;; .;;;;;;;;;;;;;;;;;;;;;;;* &;;;;;;;;;;;* ", " @@@@@@@@@+-;;;;;;;;;;;;;;*+@@@@@@@@@@@@@@@@@@@@@@@@@@@@#;;;;;;;;;;;;;;;$@@@@@@@@@+ @;;;;;;;;;;;;;;;;;;;;;;;= +;;;;;;;;* *;;;;;;;;;;;;. .;;;;;;;;;;;;;;;;;;;;;;;;= ;;;;;;;;;;;;; ", " .@@@@@@@@@#;;;;;;;;;;;;;;-@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%;;;;;;;;;;;;;;=+@@@@@@@@@ .;;;;;;;;;;;;;;;;;;;;;;;;;& +;;;;;;;;* .;;;;;;;;;;;;;& .;;;;;;;;;;;;;;;;;;;;;;;;;* #;;;;;;;;;;;;;+ ", " +@@@@@@@@@&;;;;;;;;;;;;;;#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+*;;;;;;;;;;;;;;#@@@@@@@@@ *;;;;;;;;;;;;;;;;;;;;;;;;;;# +;;;;;;;;* $;;;;;;;;;;;;;; .;;;;;;;;;;;;;;;;;;;;;;;;;;# *;;;;;;;;;;;;;= ", " @@@@@@@@@+-;;;;;;;;;;;;;*@@@@@@@@@@@@+#*;;;-&@@@@@@@@@@@@@@;;;;;;;;;;;;;;%@@@@@@@@@. +;;;;;;;;;;-# #-;;;;;;;;;;= +;;;;;;;;* =;;;;;;;;;;;;;;# .;;;;;;;;-&&&&*-;;;;;;;;;;;; ;;;;;;;;;;;;;;;. ", " @@@@@@@@@+;;;;;;;;;;;;;;@@@@@@@@@@@+%-;;;;;;;;*+@@@@@@@@@@+&;;;;;;;;;;;;;*+@@@@@@@@+ &;;;;;;;;;% %;;;;;;;;;; +;;;;;;;;* .;;;;;;;;;;;;;;;* .;;;;;;;;= #;;;;;;;;;;# %;;;;;;;;;;;;;;;$ ", ".@@@@@@@@@%;;;;;;;;;;;;;=@@@@@@@@@@+&;;;;;;;;;;;;#@@@@@@@@@@#;;;;;;;;;;;;;;+@@@@@@@@@ =;;;;;;;;= =;;;;;;;;;@ +;;;;;;;;* &;;;;;;;%;;;;;;;; .;;;;;;;;= ;;;;;;;;;& -;;;;;;;%;;;;;;;= ", ".@@@@@@@@@=;;;;;;;;;;;;;$@@@@@@@@@+*;;;;;;;;;;;;;;#@@@@@@@@@@-;;;;;;;;;;;;;#@@@@@@@@@ ;;;;;;;;;@ -;;;;;;;;. +;;;;;;;;* -;;;;;;;+*;;;;;;;% .;;;;;;;;= %;;;;;;;;- ;;;;;;;; ;;;;;;;;. ", ".@@@@@@@@@-;;;;;;;;;;;;;+@@@@@@@@@%;;;;;;;;;;;;;;;;+@@@@@@@@@%;;;;;;;;;;;;;%@@@@@@@@@ ;;;;;;;;; +;;;;;;;;* .;;;;;;;; #;;;;;;;= .;;;;;;;;= ;;;;;;;;; &;;;;;;;& &;;;;;;;& ", "+@@@@@@@@@;;;;;;;;;;;;;*+@@@@@@@@@;;;;;;;;;;;;;;;;;&@@@@@@@@@@;;;;;;;;;;;;;&@@@@@@@@@ .;;;;;;;;- +;;;;;;;;* *;;;;;;;% ;;;;;;;; .;;;;;;;;= -;;;;;;;; ;;;;;;;;. @;;;;;;;- ", "+@@@@@@@@@;;;;;;;;;;;;;&@@@@@@@@@%;;;;;;;;;;;;;;;;;-+@@@@@@@@+-;;;;;;;;;;;;*@@@@@@@@@. .;;;;;;;;- +;;;;;;;;* ;;;;;;;;. *;;;;;;;& .;;;;;;;;= =;;;;;;;;. .;;;;;;;; ;;;;;;;;. ", "+@@@@@@@@@;;;;;;;;;;;;;%@@@@@@@@+-;;;;;;;;;;;;;;;;;;$@@@@@@@@@-;;;;;;;;;;;;=@@@@@@@@@. .;;;;;;;;- .%%%%%%%%%%%%$ +;;;;;;;;* +;;;;;;;- .;;;;;;;; .;;;;;;;;= =;;;;;;;;. &;;;;;;;* %;;;;;;;* ", "@@@@@@@@@@;;;;;;;;;;;;;$@@@@@@@@@;;;;;;;;;;;;;;;;;;;&@@@@@@@@@=;;;;;;;;;;;;=+@@@@@@@@. +;;;;;;;;- -;;;;;;;;;;;;;% +;;;;;;;;* *;;;;;;;& ;;;;;;;;. .;;;;;;;;= *;;;;;;;;. ;;;;;;;;. .;;;;;;;; ", "@@@@@@@@@@;;;;;;;;;;;;;$@@@@@@@@#;;;;;;;;;;;;;;;;;;;*@@@@@@@@@=;;;;;;;;;;;;=+@@@@@@@@. +;;;;;;;;- -;;;;;;;;;;;;;& +;;;;;;;;* .;;;;;;;; *;;;;;;;& .;;;;;;;;= *;;;;;;;;+ #;;;;;;;- ;;;;;;;;+ ", "@@@@@@@@@@;;;;;;;;;;;;;$@@@@@@@@#;;;;;;;;;;;;;;;;;;;*@@@@@@@@@#$$&*-;;;;;;;=+@@@@@@@@. +;;;;;;;;- -;;;;;;;;;;;;;& +;;;;;;;;* $;;;;;;;= .;;;;;;;; .;;;;;;;;= *;;;;;;;;+ *;;;;;;;& %;;;;;;;* ", "@@@@@@@@@@;;;;;;;;;;;;;$@@@@@@@@@;;;;;;;;;;;;;;;;;;;&@@@@@@@@@@@@@@+@=;;;;;=@@@@@@@@@. .;;;;;;;;- -;;;;;;;;;;;;;& +;;;;;;;;* =;;;;;;;% -;;;;;;;# .;;;;;;;;= *;;;;;;;;. ;;;;;;;;+ ;;;;;;;;. ", "+@@@@@@@@@;;;;;;;;;;;;;%@@@@@@@@+-;;;;;;;;;;;;;;;;;;$@@@@@@@@@@@@@@@@@#-;;;=@@@@@@@@@. .;;;;;;;;- -;;;;;;;;;;;;;& +;;;;;;;;* .;;;;;;;;. &;;;;;;;* .;;;;;;;;= =;;;;;;;;. %;;;;;;;- =;;;;;;;# ", "+@@@@@@@@@;;;;;;;;;;;;;&@@@@@@@@@%;;;;;;;;;;;;;;;;;;+@@@@@+++++++@@@@@@@&;;*@@@@@@@@@. .;;;;;;;;- @%%%%*;;;;;;;;& +;;;;;;;;* &;;;;;;;=.......#;;;;;;;; .;;;;;;;;= =;;;;;;;;. -;;;;;;;%.......&;;;;;;;= ", "+@@@@@@@@@;;;;;;;;;;;;;*+@@@@@@@@@;;;;;;;;;;;;;;;;;&@@@+#%*-;;-=&$@@@@@@@%;&@@@@@@@@@ ;;;;;;;;; %;;;;;;;;% +;;;;;;;;* -;;;;;;;;;;;;;;;;;;;;;;;;% .;;;;;;;;= -;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;. ", ".@@@@@@@@@-;;;;;;;;;;;;;+@@@@@@@@@%;;;;;;;;;;;;;;;;+@@@*;;;;;;;;;;;%@@@@@@&%@@@@@@@@@ ;;;;;;;;; &;;;;;;;;% +;;;;;;;;* ;;;;;;;;;;;;;;;;;;;;;;;;;= .;;;;;;;;= ;;;;;;;;; %;;;;;;;;;;;;;;;;;;;;;;;;;& ", ".@@@@@@@@@=;;;;;;;;;;;;;$@@@@@@@@@+*;;;;;;;;;;;;;;#@+%;;;;;;;;;;;;;;-@@@@@@@@@@@@@@@@ -;;;;;;;;# =;;;;;;;;@ +;;;;;;;;* *;;;;;;;;;;;;;;;;;;;;;;;;;; .;;;;;;;;= $;;;;;;;;- ;;;;;;;;;;;;;;;;;;;;;;;;;;- ", ".@@@@@@@@@%;;;;;;;;;;;;;=@@@@@@@@@@+*;;;;;;;;;;;;#@+*;;;;;;;;;;;;;;;;;$+@@@@@@@@@@@@@ =;;;;;;;;= @;;;;;;;;; +;;;;;;;;* ;;;;;;;;;;;;;;;;;;;;;;;;;;;& .;;;;;;;;= -;;;;;;;;& .;;;;;;;;;;;;;;;;;;;;;;;;;;;. ", " @@@@@@@@@+;;;;;;;;;;;;;;@@@@@@@@@@@+%-;;;;;;;;*@@+*;;;;;;;;;;;;;;;;;;;$@@@@@@@@@@@@+ &;;;;;;;;;% +;;;;;;;;;= +;;;;;;;;* +;;;;;;;;;;;;;;;;;;;;;;;;;;;; .;;;;;;;;= +-;;;;;;;;;$ &;;;;;;;;;;;;;;;;;;;;;;;;;;;* ", " @@@@@@@@@+-;;;;;;;;;;;;;*@@@@@@@@@@@@+$=;;;-&@@@@&;;;;;;;;;;;;;;;;;;;;;@@@@@@@@@@@@. .;;;;;;;;;;-@ .*;;;;;;;;;;% +;;;;;;;;* *;;;;;;;;;;;;;;;;;;;;;;;;;;;;. .;;;;;;;;-&&&&&*;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ", " +@@@@@@@@@*;;;;;;;;;;;;;;#@@@@@@@@@@@@@@@@@+@@@@@;;;;;;;;;;;;;;;;;;;;;;-@@@@@@@@@@@ &;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;* .;;;;;;;;;;;;;;;;;;;;;;;;;;;;;& .;;;;;;;;;;;;;;;;;;;;;;;;;;$ #;;;;;;;;;;;;;;;;;;;;;;;;;;;;;. ", " .@@@@@@@@@#;;;;;;;;;;;;;;-@@@@@@@@@@@@@@@@@@@@@@-;;;;;;;;;;;;;;;;;;;;;;;$@@@@@@@@@@ .;;;;;;;;;;;;;;;;;;;;;;;;;@ +;;;;;;;;* $;;;;;;;;% ;;;;;;;;; .;;;;;;;;;;;;;;;;;;;;;;;;;- *;;;;;;;; .;;;;;;;;* ", " @@@@@@@@@+-;;;;;;;;;;;;;;*+@@@@@@@@@@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;-+@@@@@@@@+ @;;;;;;;;;;;;;;;;;;;;;;;& +;;;;;;;;* *;;;;;;;; &;;;;;;;;# .;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;= -;;;;;;;;.", " +@@@@@@@@@%;;;;;;;;;;;;;;;&@@@@@@@@@@@@@@@@@@+%;;;;;;;;;;;;;;;;;;;;;;;;;#@@@@@@@@. #;;;;;;;;;;;;;;;;;;;;;% +;;;;;;;;* ;;;;;;;;* ;;;;;;;;* .;;;;;;;;;;;;;;;;;;;;;;;- %;;;;;;;;% %;;;;;;;;#", " .@@@@@@@@@@;;;;;;;;;;;;;;;;&+@@@@@@@@@@@@@@@@+-;;;;;;;;;;;;;;;;;;;;;;;;;%@@@@@@@@ .-;;;;;;;;;;;;;;;;;;+ +;;;;;;;;* &;;;;;;;;$ =;;;;;;;; .;;;;;;;;;;;;;;;;;;;;;;% -;;;;;;;; ;;;;;;;;*", " @@@@@@@@@@%;;;;;;;;;;;;;;;;*@@@@@@@@@@@@@@@@+;;;;;;;;;;;;;;;;;;;;;;;;;;&+@@@@@@+ $=;;;;;;;;;;;;;=$ .;;;;;;;;* =;;;;;;;; $;;;;;;;;# .;;;;;;;;;;;;;;;;;;;=%. ;;;;;;;;& *;;;;;;;;", " +@@@@@@@@@@-;;;;;;;;;;;;;;;;-#+@@@@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;;;*+@@@@@@. .%=---;---=%. %&&&&&&&. +&&&&&&&. $&&&&&&% #&&&&&&&&&&&&&&%$+ $&&&&&&% %&&&&&&#", " .@@@@@@@@@@#;;;;;;;;;;;;;;;;;;*@@@@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;;;=+@@@@@@ ", " +@@@@@@@@@@*;;;;;;;;;;;;;;;;;;;=%++@@@@@@@+@;;;;;;;;;;;;;;;;;;;;;;;;;;*+@@@@@. ", " .@@@@@@@@@@@-;;;;;;;;;;;;;;;;;;;;;=&%%$%%&*-;;;;;;;;;;;;;;;;;;;;;;;;;;&+@@@@@ ", " +@@@@@@@@@@#;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%@@@@@. ", " @@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;#@@@@+ ", " +@@@@@@@@@@@%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-+@@@@ ", " @@@@@@@@@@@+&;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;$@@@@. ", " .@@@@@@@@@@@+%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-@@@@@ ", " .@@@@@@@@@@@@$;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;@@@@@ ", " +@@@@@@@@@@@@#-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;$@@@@. ", " @@@@@@@@@@@@@@*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%+@@@+ ", " @@@@@@@@@@@@@+#-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-#@@@@+ ", " .@@@@@@@@@@@@@@@%;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;%@@@@@@ ", " .@@@@@@@@@@@@@@@@$-;;;;;;;;;;;;;;;;;;;;;;;;*$%*-;;;-*$@@@@@@@ ", " .@@@@@@@@@@@@@@@@+#&-;;;;;;;;;;;;;;;;;;=%@+@@+++.+++@@@@@@+ ", " @@@@@@@@@@@@@@@@@@++%*--;;;;;;;;--&#++@@@@@@@@@@@@@@@@@+ ", " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ", " .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ", " .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+ ", " +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ", " @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ", " .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ", " .+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@. ", " .+@@@@@@@@@@@@@@@@@@@@@@@@@@+. ", " .+@@@@@@@@@@@@@@@@@@@@. ", " ...+++@@@@++... "}; const char* loopRepeat_xpm[] = { "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", "..................", "..................", "..................", "......#%&&%#......", "....+%&&&&&&%+....", "....%&&&%%&&&%....", "...#&&%+..+%&&#...", "...%&&+....+&&%...", "...&&%......%&&...", "...&&%......%&&...", "...%&&+....+&&%...", "...#&&%+..+%&&#...", "....%&&&%%&&&%....", "....+%&&&&&&%+....", "......#%&&%#......", "..................", "..................", ".................."}; const char* loopOnce_xpm[] = { "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", " ", " ", " ", " @$&%#@ ", " .$&&&&&&$. ", " %&&#@@#&&$ ", " @&&@ @&&@ ", " %&# +%$+ #&$ ", " %&&% ", " %&&% ", " $&# +%%+ #&$ ", " @&&@ @&&@ ", " $&&#@@#&&$ ", " .$&&&&&&$. ", " @#&&#@ ", " ", " ", " "}; const char* oneshotBasic_xpm[] = { "18 18 8 1", " c #181917", ". c #242523", "+ c #313230", "@ c #4D4F4C", "# c #666765", "$ c #787A77", "% c #919390", "& c #BEC0BD", "..................", "..................", "..................", "..................", "..................", "..................", "..................", "..................", "..................", "..................", "..................", "..................", "...$$$$$$$$$$$$...", "...&&&&&&&&&&&&...", "...&&&&&&&&&&&&...", "..................", "..................", ".................."}; const char* oneshotBasicPause_xpm[] = { "18 18 5 1", " c #252525", ". c #4E4E4E", "+ c #767676", "@ c #9F9F9F", "# c #C8C8C8", " ", " ", " ", " +## ", " +## ", " +## ", " +## ", " +## ", " +## ", " +## ", " +## ", " +## ", " +++++++++@## ", " ############ ", " ############ ", " ", " ", " "}; const char* oneshotRetrig_xpm[] = { "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", "..................", "..................", "..................", "..................", "..................", "..................", "...+%&%+..........", "...%&&&%..........", "...&&&&&..........", "...$&&&$..........", "...+$&$+..........", "..................", "...$$$$$$$$$$$$...", "...&&&&&&&&&&&&...", "...&&&&&&&&&&&&...", "..................", "..................", ".................."}; const char* oneshotEndless_xpm[] = { "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", " ", " ", " ", " ", " @@ ", " #&&# ", " .#&&&&#. ", " .$&&&&&&$. ", " +@%&&&&%@+ ", " #&&&&# ", " #&&&&# ", " #&&&&# ", " #&&&&# ", " #&&&&# ", " .... ", " ", " ", " "}; const char* updirOn_xpm[] = { "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", " ", " ", " ", " ", " ", " #+ ", " &%#. ", " &&&%@ ", " &&&&&$+ ", " &&&&&&&#. ", " &&&&&&&&%@ ", " &&&&&&&&&&# ", " &&&&&&&&%@. ", " &&&&&&&#. ", " &&&&&$+ ", " &&&%@ ", " &&#. ", " $+ ", " ", " ", " ", " ", " "}; const char* play_xpm[] = { "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", " ", " ", " ", " ", " ", " .$ ", " @&& ", " +%&&& ", " .$&&&&& ", " @&&&&&&& ", " +%&&&&&&&& ", " #&&&&&&&&&& ", " +%&&&&&&&& ", " .#&&&&&&& ", " .$&&&&& ", " +%&&& ", " @&& ", " .$ ", " ", " ", " ", " ", " "}; const char* rewindOn_xpm[] = { "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", " ", " &&&&&&&&&&&&&&&&&&&&%$@ .@$%&&&&&&&&&&&&&&&&&&&& ", " &&&&&&&&&&&&&&&&&%$@. .#%&&&&&&&&&&&&&&&&&& ", " &&&&&&&&&&&&&&&&#+ .@$&&&&&&&&&&&&&&&& ", " &&&&&&&&&&&&&&$+ .@%&&&&&&&&&&&&&& ", " &&&&&&&&&&&&&@ .#&&&&&&&&&&&&& ", " &&&&&&&&&&&%. @&&&&&&&&&&&& ", " &&&&&&&&&&$ ...+@@#@@+.. +%&&&&&&&&&& ", " &&&&&&&&&# .+#$&&&&&&&&&&%$@+. .%&&&&&&&&& ", " &&&&&&&&# +#%&&&&&&&&&&&&&&&&&$@. .%&&&&&&&& ", " &&&&&&&# .#%&&&&&&&&&&&&&&&&&&&&&%@ .%&&&&&&& ", " &&&&&&$ @%&&&&&&&&&&&&&&&&&&&&&&&&&%+ .&&&&&&& ", " &&&&&% $&&&&&&&&&&&&&&&&&&&&&&&&&&&&&@ +&&&&&& ", " &&&&&. .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# @&&&&& ", " &&&&@ +%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# $&&&& ", " &&&$ .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# +&&&& ", " &&&+ %&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# #&&& ", " &&# $&&&&&&&&&&&&&&&&%$###$%&&&&&&&&&&&&&&&&@ +%&& ", " &%+ @&&&&&&&&&&&&&&%@.. ..+#&&&&&&&&&&&&&&%. #&& ", " &$ .%&&&&&&&&&&&&%+ #&&&&&&&&&&&&&# +&& ", " &@ $&&&&&&&&&&&&# +%&&&&&&&&&&&%+ $& ", " %. +%&&&&&&&&&&&@ .$&&&&&&&&&&&# @& ", " $ #&&&&&&&&&&&@ .$&&&&&&&&&&&+ & ", " @ .%&&&&&&&&&&# .%&&&&&&&&&&@ $ ", " . +&&&&&&&&&&% +&&&&&&&&&&%. @ ", " #&&&&&&&&&&+ @%&&%$+ #&&&&&&&&&&. . ", " .%&&&&&&&&&$ +%&&&&&&&# .&&&&&&&&&&@ ", " .&&&&&&&&&&+ +&&&&&&&&&&$ $&&&&&&&&&$ ", " +&&&&&&&&&& .%&&&&&&&&&&&# @&&&&&&&&&% ", " @&&&&&&&&&$ @&&&&&&&&&&&&&. +&&&&&&&&&& ", " @&&&&&&&&&# %&&&&&&&&&&&&&# .%&&&&&&&&&. ", " #&&&&&&&&&@ .&&&&&&&&&&&&&&$ .%&&&&&&&&&. ", " #&&&&&&&&&@ .&&&&&&&&&&&&&&% .#$%&&&&&&&+ ", " #&&&&&&&&&@ .&&&&&&&&&&&&&&$ @%&&&&. ", " @&&&&&&&&&# %&&&&&&&&&&&&&# @%&&. ", " @&&&&&&&&&$ #&&&&&&&&&&&&&. +@@@@+. .$& ", " +&&&&&&&&&& .%&&&&&&&&&&&# +$%&&&&&%#. .$ ", " .&&&&&&&&&&+ +&&&&&&&&&&$ $&&&&&&&&&&%@ . ", " .%&&&&&&&&&$ +%&&&&&&&#. %&&&&&&&&&&&&&# ", " #&&&&&&&&&&+ @%&&&$+ $&&&&&&&&&&&&&&&@ . ", " . +&&&&&&&&&&% @&&&&&&&&&&&&&&&&%. @ ", " @ .%&&&&&&&&&&# %&&&&&&&&&&&&&&&&&@ $ ", " $ #&&&&&&&&&&&@ @&&&&&&&&&&&&&&&&&&%. & ", " %. +%&&&&&&&&&&&@ $&&&&&&&&&&&&&&&&&&&. @& ", " &@ $&&&&&&&&&&&&# %&&&&&&&&&&&&&&&&&&&+ $& ", " &$ .%&&&&&&&&&&&&%+ %&&&&&&&&&&&&&&&&&&&@ +&& ", " &%+ @&&&&&&&&&&&&&&%@.. .%&&&&&&&&&&&&&&&&&&&@ #&& ", " &&# $&&&&&&&&&&&&&&&&%$###$%&&&&&&&&&&&&&&&&&&&&+ +%&& ", " &&&+ .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&. #&&& ", " &&&$ .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# +&&&& ", " &&&&@ +%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&+ $&&&& ", " &&&&&. .%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&# @&&&&& ", " &&&&&% .$&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%. +&&&&&& ", " &&&&&&$ @%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%. .&&&&&&& ", " &&&&&&&# .$%&&&&&&&&&&&&&&&&&&&&&&&&&&&&%# .%&&&&&&& ", " &&&&&&&&# +#%&&&&&&&&&&&&&&&&&$@#$%%$$@+ .%&&&&&&&& ", " &&&&&&&&&# .+#%&&&&&&&&&&&$@+. .... .%&&&&&&&&& ", " &&&&&&&&&&$ ..+@@###@+... +%&&&&&&&&&& ", " &&&&&&&&&&&%. @&&&&&&&&&&&& ", " &&&&&&&&&&&&&@ .#&&&&&&&&&&&&& ", " &&&&&&&&&&&&&&$+ .@%&&&&&&&&&&&&&& ", " &&&&&&&&&&&&&&&&#+ .@$&&&&&&&&&&&&&&&& ", " &&&&&&&&&&&&&&&&&%$@. .#%&&&&&&&&&&&&&&&&&& ", " &&&&&&&&&&&&&&&&&&&&%$@ .@$%&&&&&&&&&&&&&&&&&&&& ", " "}; const char* recOff_xpm[] = { "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", " ", " ", " ", " ", " ", " @$&&$@ ", " .%&&&&&&%. ", " %&&&&&&&&% ", " @&&&#++#&&&@ ", " $&&# #&&$ ", " &&&+ +&&& ", " &&&+ +&&& ", " $&&# #&&$ ", " @&&&#++#&&&@ ", " %&&&&&&&&% ", " .%&&&&&&%. ", " @$&&$@ ", " ", " ", " ", " ", " ", " "}; const char* inputRecOn_xpm[] = { "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", " ", " ", " ", " ", " ", " .@#%%#@. ", " +$&&&&&&$+ ", " .$&&&&&&&&$. ", " @&&&&&&&&&&@ ", " #&&&&&&&&&&# ", " %&&&&&&&&&&% ", " %&&&&&&&&&&% ", " #&&&&&&&&&&# ", " @&&&&&&&&&&@ ", " $&&&&&&&&$ ", " +$&&&&&&$+ ", " .@#%%#@. ", " ", " ", " ", " ", " ", " "}; 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", " ", " ", " ", " ", " ++. .++ ", " +&&$ $&&+ ", " +&&% %&&+ ", " +&%&++&%&+ ", " +&$&##&$&+ ", " +&#%$$%#&+ ", " +&#$%%$#&+ ", " +&#@&&@#&+ ", " +&#+&&+#&+ ", " .#@ ## @#. ", " ", " ", " ", " "}; const char* muteOn_xpm[] = { "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", " ", " ", " ", " ", " .... ", " %&&&&%+ ", " %&@@@&& ", " %% $&. ", " %&@@#&$ ", " %&&&&@ ", " %% +&$ ", " %% #&# ", " %% %&+ ", " @@ .#+ ", " ", " ", " ", " "}; const char* readActionOn_xpm[] = { "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", "..................", "..................", "..................", "..................", ".....@@@@+........", ".....%%%%%%+......", ".....%%#+$%$......", ".....%%@.#%$......", ".....%%##%%@......", ".....%%%%%$.......", ".....%%@+%%#......", ".....%%@.@%%......", ".....%%@..%%#.....", ".....@@...+@@.....", "..................", "..................", "..................", ".................."}; const char* metronomeOff_xpm[] = { "13 23 3 1", " c None", ". c #252525", "+ c #B18E8E", ".............", ".............", ".............", ".............", ".............", ".............", "....+...+....", "....+...+....", "....+...+....", "....+...+....", "....+...+....", "....+...+....", "....+...+....", "....+...+....", "....+...+....", "....+...+....", ".............", ".............", ".............", ".............", ".............", ".............", "............."}; const char* metronomeOn_xpm[] = { "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", " ", " ", " ", " ", " ", " @$@ ", " %&% ", " $&$ ", " .+. ", " ", " #%# ", " %&% ", " #%# ", " ", " .+. ", " $&$ ", " %&% ", " @$@ ", " ", " ", " ", " ", " "}; const char* recTriggerModeOn_xpm[] = { "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", "++++++++++++++++++", "+................+", "+................+", "+................+", "+................+", "+.......@@.......+", "+.......#$.......+", "+.......#$.......+", "+....@%%&&%%@....+", "+....@%%&&%%@....+", "+.......#$.......+", "+.......#$.......+", "+.......@@.......+", "+................+", "+................+", "+................+", "+................+", "++++++++++++++++++"}; const char* zoomInOn_xpm[] = { "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", "++++++++++++++++++", "+................+", "+................+", "+................+", "+................+", "+................+", "+................+", "+................+", "+......@##@......+", "+......@##@......+", "+................+", "+................+", "+................+", "+................+", "+................+", "+................+", "+................+", "++++++++++++++++++"}; const char* zoomOutOn_xpm[] = { "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", "............", "............", "...+........", "...&$@......", "...$&&%@....", "....+#%&%...", "....+#%&%...", "...$&&%#....", "...&$@......", "...+........", "............", "............"}; const char* scrollLeftOff_xpm[] = { "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", " ", " ", " ", " .@$ ", " +#%%@ ", " $%#+ ", " %%#+ ", " +$%%@ ", " .#$ ", " ", " ", " "}; const char* scrollRightOn_xpm[] = { "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", " ", " ", " ", " ", " .@+. ", " #&&&&# ", " .&$ %&. ", " &%+ .. ", " #&&&$. ", " .@$&&. ", " .#. @&@ ", " .&$. #&+ ", " #&&&&$ ", " .+@+ ", " ", " ", " ", " "}; const char* soloOff_xpm[] = { "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", " ", " ", " ", " ", " ", " ..... . . ", " $&&&$ $% @&. ", " $$ .&#&@ ", " $%##. @&$ ", " $%##. #&% ", " $$ .&@&# ", " $$ %$ @&. ", " .. + +. ", " ", " ", " ", " ", " "}; const char* fxOn_xpm[] = { "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", " ", " ", " ", " ", " ", " ", " .+@ ", " @+#. ", " $#%+@ ", " %# %#$ ", " +@ $#% ", " $#. @+ ", " $. $. ", " ", " ", " ", " ", " "}; const char* fxShiftUpOn_xpm[] = { "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", " ", " ", " ", " ", " ", " ", " .+@ #$ ", " @%# +$ ", " $+ .%@ ", " .%@$+ ", " +$%# ", " #%%@ ", " @.. ", " ", " ", " ", " ", " "}; const char* fxShiftDownOn_xpm[] = { "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", " @#############################################################+ ", "@#.............................................................$+", "#. .#", "#. .#", "#. ...... .. .#", "#. .@$$$####$%$#@.+&$ .#", "#. .#$$#+. +#$%%%%$ .#", "#. .$$#$ .#$$%$ .#", "#. ............. ....$$$$$ ++$$%$+@@@@@@@@@@@@@@@ .#", "#. ##$$$$$$%%%%@ %%&&&&%%$@ %&@#$$@@$%%&&&%%%&&&&& .#", "#. +$$$$$%@ .&%####%$@ $&$.$# #$%%%& @&%& .#", "#. +$$$$$% +&$###$%%&&$@. $&. #$%%%&. .%& .#", "#. @$$$$%$ %##$##$%&&&&&&%#.%# #$$%%&. @& .#", "#. $$$$$%+ #& #$$%%&&&&&&&%%$$@ #$$%%&. + .#", "#. .$$$$$% +&+ .#%&&&&&&&&%$$#$$# #$$%%&. .#", "#. @$$$$%$ %$ @%&&&&&&%$$###$$ #$$%%&. .#", "#. #$$$%%@ #& . +$&&&%$####$%$ #$$%%&. .#", "#. $$$%%% .&@ +%# .@$$$###$$% #$$$%&. .#", "#. +%$%%%$$% +$$+ #$#$$$% @$$$%&. .#", "#. #%%%%%&. +%$$ ##$$%$ @$$$%%. .#", "#. $$%%%@ +%$$$. #$$$%. @$$$$%. .#", "#. +%%%$ +%$$#$@ +$$%$ @#$$$%+ .#", "#. @%%. +%%%$$$$#@++.++@#$$$@ @@##$$$%%%$$@ .#", "#. #@ +&# .@@###$$$###@. @+++@@@@###$@ .#", "#. .#", "#. .#", "#. .#", "#. .#", "#. .#", "#. .@$$$$$$$$ .$%%%%%%# .#", "#. ....... .@@@@@@@@@. .#", "#. ........ @@@+@@@@@@@@@@+ .#", "@# ......... .####@@@@@@@@@@@+ #@", " @$$$$$$$$$$$$$$$.......... .@@@@@@@@@@@@@@@@@$$$$$$$$$$$$$$$$@ ", " ......... .@@@@@@@@@@@@@@@. ", " ........ @@@@@@@@@@. ", " ........... .@@@@@@@@@ ", " .......... .@@@@@@@@ "}; 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", "..................", "..................", "..................", "..................", "..................", ".....+#@..@#+.....", "......&*++*&......", "......@*%%*@......", ".......$**$.......", ".......#**#.......", "......+*&&*+......", "......%*@@*%......", "......@@..@@......", "..................", "..................", "..................", "..................", ".................."}; 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", "..................", "..................", "..................", "..................", "..................", "......#@..@#......", "......&*++*&......", "......@*%%*@......", ".......$**$.......", ".......#**#.......", "......+*&&*+......", "......%*@+*%......", "......@+..+@......", "..................", "..................", "..................", "..................", ".................."}; #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", " ", " ", " ", " ", " ", " @@ ", " %$ ", " ++ ", " .########. ", " .########. ", " ++ ", " %$ ", " @@ ", " ", " ", " ", " ", " "}; const char* divideOff_xpm[] = { "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", " ", " ", " ", " ", " ", " + . ", " +&$ #&. ", " #&$#&# ", " @&&# ", " @&&% ", " @&#@&% ", " +&# #&+ ", " + . ", " ", " ", " ", " ", " "}; const char* multiplyOff_xpm[] = { "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", " ", " ", " ", " ", " ##. ", " $&%@ ", " $&&&%+ ", " $&&&&&$. ", " $&&&&&&&@ ", " $&&&&&&&@. ", " $&&&&&$. ", " $&&&%+ ", " $&&@ ", " $#. ", " . ", " ", " ", " "}; const char* channelPlay_xpm[] = { "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", " ", " ", " ", " ", " +#%%#. ", " @&&&&&&@ ", " +&&&&&&&&. ", " #&&&&&&&&# ", " %&&&&&&&&% ", " %&&&&&&&&% ", " #&&&&&&&&# ", " .&&&&&&&&. ", " @&&&&&&@ ", " .#%%#. ", " ", " ", " ", " "}; const char* armOn_xpm[] = { "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", "..................", "..................", "..................", "..................", "......+#$$#+......", ".....@%%%%%%@.....", "....+%%%%%%%%+....", "....#%%%%%%%%#....", "....$%%%%%%%%$....", "....$%%%%%%%%$....", "....#%%%%%%%%#....", "....+%%%%%%%%+....", ".....@%%%%%%@.....", "......+#$$#+......", "..................", "..................", "..................", ".................."};giada-0.22.0/src/core/graphics.h000066400000000000000000000066261425106661500163500ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * graphics * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_GRAPHICS_H #define G_GRAPHICS_H extern const char* giada_logo_xpm[]; extern const char* loopRepeat_xpm[]; extern const char* loopBasic_xpm[]; extern const char* loopOnce_xpm[]; extern const char* loopOnceBar_xpm[]; extern const char* oneshotBasic_xpm[]; extern const char* oneshotBasicPause_xpm[]; extern const char* oneshotRetrig_xpm[]; extern const char* oneshotPress_xpm[]; extern const char* oneshotEndless_xpm[]; extern const char* updirOff_xpm[]; extern const char* updirOn_xpm[]; extern const char* pause_xpm[]; extern const char* play_xpm[]; extern const char* zoomInOff_xpm[]; extern const char* zoomInOn_xpm[]; extern const char* zoomOutOff_xpm[]; extern const char* zoomOutOn_xpm[]; extern const char* scrollLeftOff_xpm[]; extern const char* scrollLeftOn_xpm[]; extern const char* scrollRightOff_xpm[]; extern const char* scrollRightOn_xpm[]; extern const char* rewindOff_xpm[]; extern const char* rewindOn_xpm[]; extern const char* recOff_xpm[]; extern const char* recOn_xpm[]; extern const char* metronomeOff_xpm[]; extern const char* metronomeOn_xpm[]; extern const char* recTriggerModeOff_xpm[]; extern const char* recTriggerModeOn_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* multiplyOff_xpm[]; extern const char* muteOff_xpm[]; extern const char* muteOn_xpm[]; extern const char* soloOff_xpm[]; extern const char* soloOn_xpm[]; extern const char* armOff_xpm[]; extern const char* armOn_xpm[]; extern const char* armDisabled_xpm[]; extern const char* readActionOn_xpm[]; extern const char* readActionOff_xpm[]; extern const char* readActionDisabled_xpm[]; extern const char* channelStop_xpm[]; extern const char* channelPlay_xpm[]; #ifdef WITH_VST extern const char* fxOff_xpm[]; extern const char* fxOn_xpm[]; extern const char* fxShiftUpOn_xpm[]; extern const char* fxShiftUpOff_xpm[]; extern const char* fxShiftDownOn_xpm[]; extern const char* fxShiftDownOff_xpm[]; extern const char* fxRemoveOff_xpm[]; extern const char* fxRemoveOn_xpm[]; extern const char* vstLogo_xpm[]; #endif extern const char* giada_icon[]; #endif giada-0.22.0/src/core/idManager.cpp000066400000000000000000000034621425106661500167650ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "idManager.h" namespace giada::m { 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() const { return m_id; } /* -------------------------------------------------------------------------- */ ID IdManager::getNext() const { return m_id + 1; } } // namespace giada::m giada-0.22.0/src/core/idManager.h000066400000000000000000000035331425106661500164310ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_ID_MANAGER_H #define G_ID_MANAGER_H #include "core/types.h" namespace giada::m { class IdManager { public: IdManager(); /* set Stores a new id, only if != 0 (valid) and greater than current id (unique). */ void set(ID id); /* 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 generate(ID id = 0); /* get Returns the current id, a.k.a. the last generated one. */ ID get() const; /* getNext Returns the upcoming ID, that is the ID that will be generated on the next call. */ ID getNext() const; private: ID m_id; }; } // namespace giada::m #endif giada-0.22.0/src/core/init.cpp000066400000000000000000000102341425106661500160340ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef __APPLE__ #include #endif #include "core/engine.h" #include "gui/dialogs/warnings.h" #include "gui/ui.h" #include "gui/updater.h" #include "utils/log.h" #include "utils/ver.h" #ifdef WITH_TESTS #define CATCH_CONFIG_RUNNER #include "tests/actionRecorder.cpp" #include "tests/midiLighter.cpp" #include "tests/samplePlayer.cpp" #include "tests/utils.cpp" #include "tests/wave.cpp" #include "tests/waveFx.cpp" #include "tests/waveManager.cpp" #include "tests/waveReader.cpp" #include #include #include #endif #include extern giada::m::Engine g_engine; extern giada::v::Ui g_ui; namespace giada::m::init { int tests(int argc, char** argv) { #ifdef WITH_TESTS std::vector args(argv, argv + argc); if (args.size() > 1 && strcmp(args[1], "--run-tests") == 0) return Catch::Session().run(args.size() - 1, &args[1]); else return -1; #else (void)argc; (void)argv; return -1; #endif } /* -------------------------------------------------------------------------- */ void printBuildInfo() { u::log::print("[init] Giada %s\n", G_VERSION_STR); u::log::print("[init] Build date: " BUILD_DATE "\n"); #ifdef G_DEBUG_MODE u::log::print("[init] Debug build\n"); #else u::log::print("[init] Release build\n"); #endif u::log::print("[init] Dependencies:\n"); u::log::print("[init] FLTK - %d.%d.%d\n", FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION); u::log::print("[init] RtAudio - %s\n", u::ver::getRtAudioVersion()); u::log::print("[init] RtMidi - %s\n", u::ver::getRtMidiVersion()); 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); #ifdef WITH_VST u::log::print("[init] JUCE - %d.%d.%d\n", JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_BUILDNUMBER); #endif KernelAudio::logCompiledAPIs(); KernelMidi::logCompiledAPIs(); } /* -------------------------------------------------------------------------- */ void startup(int argc, char** argv) { #ifdef WITH_VST juce::initialiseJuce_GUI(); #endif g_engine.init(); g_ui.init(argc, argv, g_engine); if (!g_engine.kernelAudio.isReady()) v::gdAlert(g_ui.langMapper.get(v::LangMap::MESSAGE_INIT_WRONGSYSTEM)); } /* -------------------------------------------------------------------------- */ int run() { Fl::lock(); // Enable multithreading in FLTK return Fl::run(); } /* -------------------------------------------------------------------------- */ void closeMainWindow() { if (!v::gdConfirmWin(g_ui.langMapper.get(v::LangMap::COMMON_WARNING), g_ui.langMapper.get(v::LangMap::MESSAGE_INIT_QUITGIADA))) return; shutdown(); } /* -------------------------------------------------------------------------- */ void shutdown() { g_ui.shutdown(); g_engine.shutdown(); #ifdef WITH_VST juce::shutdownJuce_GUI(); #endif u::log::print("[init] Giada %s closed\n\n", G_VERSION_STR); } } // namespace giada::m::init giada-0.22.0/src/core/init.h000066400000000000000000000027521425106661500155070ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_INIT_H #define G_INIT_H namespace giada::m::init { /* tests Performs tests, if requested. Returns -1 if no tests are available or the `--run-tests` has not been passed in. */ int tests(int argc, char** argv); void printBuildInfo(); void startup(int argc, char** argv); int run(); void closeMainWindow(); void shutdown(); } // namespace giada::m::init #endifgiada-0.22.0/src/core/jackTransport.cpp000066400000000000000000000076501425106661500177260ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "jackTransport.h" #ifdef WITH_AUDIO_JACK #include #include #endif namespace giada::m { bool JackTransport::State::operator!=(const State& o) const { return !(running == o.running && bpm == o.bpm && frame == o.frame); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ JackTransport::JackTransport() #ifdef WITH_AUDIO_JACK : m_jackHandle(nullptr) #endif { } /* -------------------------------------------------------------------------- */ bool JackTransport::start() const { #ifdef WITH_AUDIO_JACK if (m_jackHandle == nullptr) return false; jack_transport_start(m_jackHandle); return true; #else return false; #endif } /* -------------------------------------------------------------------------- */ bool JackTransport::stop() const { #ifdef WITH_AUDIO_JACK if (m_jackHandle == nullptr) return false; jack_transport_stop(m_jackHandle); return true; #else return false; #endif } /* -------------------------------------------------------------------------- */ bool JackTransport::setPosition(uint32_t frame) const { #ifdef WITH_AUDIO_JACK if (m_jackHandle == nullptr) return false; jack_position_t position; jack_transport_query(m_jackHandle, &position); position.frame = frame; jack_transport_reposition(m_jackHandle, &position); return true; #else (void)frame; return false; #endif } /* -------------------------------------------------------------------------- */ bool JackTransport::setBpm(double bpm) const { #ifdef WITH_AUDIO_JACK if (m_jackHandle == nullptr) return false; jack_position_t position; jack_transport_query(m_jackHandle, &position); position.valid = jack_position_bits_t::JackPositionBBT; position.bar = 0; // no such info from Giada position.beat = 0; // no such info from Giada position.tick = 0; // no such info from Giada position.beats_per_minute = bpm; jack_transport_reposition(m_jackHandle, &position); return true; #else (void)bpm; return false; #endif } /* -------------------------------------------------------------------------- */ JackTransport::State JackTransport::getState() const { #ifdef WITH_AUDIO_JACK if (m_jackHandle == nullptr) return {}; jack_position_t position; jack_transport_state_t ts = jack_transport_query(m_jackHandle, &position); return { ts != JackTransportStopped, position.beats_per_minute, position.frame}; #else return {}; #endif } /* -------------------------------------------------------------------------- */ #ifdef WITH_AUDIO_JACK void JackTransport::setHandle(jack_client_t* h) { m_jackHandle = h; } #endif } // namespace giada::m giada-0.22.0/src/core/jackTransport.h000066400000000000000000000040071425106661500173640ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_JACK_TRANSPORT_H #define G_JACK_TRANSPORT_H #ifdef WITH_AUDIO_JACK #include #endif #include namespace giada::m { class JackTransport final { public: struct State { bool running; double bpm; uint32_t frame; bool operator!=(const State& o) const; }; JackTransport(); bool start() const; bool stop() const; bool setPosition(uint32_t frame) const; bool setBpm(double bpm) const; State getState() const; #ifdef WITH_AUDIO_JACK void setHandle(jack_client_t*); #endif private: /* m_jackHandle Optional handle to JACK. If nullptr the JackTransport class is not initialized and all public transport methods above will return false. This is useful when you are on a platform that supports JACK (e.g. Linux) but the JACK API is currently not selected. */ #ifdef WITH_AUDIO_JACK jack_client_t* m_jackHandle; #endif }; } // namespace giada::m #endif giada-0.22.0/src/core/kernelAudio.cpp000066400000000000000000000265651425106661500173510ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * KernelAudio * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/kernelAudio.h" #include "core/conf.h" #include "core/const.h" #include "utils/log.h" #include "utils/vector.h" #include #include namespace giada::m { KernelAudio::KernelAudio() : onAudioCallback(nullptr) , m_ready(false) , m_inputEnabled(false) , m_realBufferSize(0) , m_realSampleRate(0) , m_channelsOutCount(0) , m_channelsInCount(0) , m_api(0) { } /* -------------------------------------------------------------------------- */ int KernelAudio::openDevice(const Conf::Data& conf) { assert(onAudioCallback != nullptr); m_api = conf.soundSystem; u::log::print("[KA] using system 0x%x\n", m_api); #if defined(__linux__) || defined(__FreeBSD__) if (m_api == G_SYS_API_JACK && hasAPI(RtAudio::UNIX_JACK)) m_rtAudio = std::make_unique(RtAudio::UNIX_JACK); else if (m_api == G_SYS_API_ALSA && hasAPI(RtAudio::LINUX_ALSA)) m_rtAudio = std::make_unique(RtAudio::LINUX_ALSA); else if (m_api == G_SYS_API_PULSE && hasAPI(RtAudio::LINUX_PULSE)) m_rtAudio = std::make_unique(RtAudio::LINUX_PULSE); #elif defined(__FreeBSD__) if (m_api == G_SYS_API_JACK && hasAPI(RtAudio::UNIX_JACK)) m_rtAudio = std::make_unique(RtAudio::UNIX_JACK); else if (m_api == G_SYS_API_PULSE && hasAPI(RtAudio::LINUX_PULSE)) m_rtAudio = std::make_unique(RtAudio::LINUX_PULSE); #elif defined(_WIN32) if (m_api == G_SYS_API_DS && hasAPI(RtAudio::WINDOWS_DS)) m_rtAudio = std::make_unique(RtAudio::WINDOWS_DS); else if (m_api == G_SYS_API_ASIO && hasAPI(RtAudio::WINDOWS_ASIO)) m_rtAudio = std::make_unique(RtAudio::WINDOWS_ASIO); else if (m_api == G_SYS_API_WASAPI && hasAPI(RtAudio::WINDOWS_WASAPI)) m_rtAudio = std::make_unique(RtAudio::WINDOWS_WASAPI); #elif defined(__APPLE__) if (m_api == G_SYS_API_CORE && hasAPI(RtAudio::MACOSX_CORE)) m_rtAudio = std::make_unique(RtAudio::MACOSX_CORE); #endif else { u::log::print("[KA] No API available, nothing to do!\n"); return 0; } m_rtAudio->setErrorCallback([](RtAudioErrorType type, const std::string& msg) { u::log::print("[KA] RtAudio error %d: %s\n", type, msg.c_str()); }); u::log::print("[KA] Opening device out=%d, in=%d, samplerate=%d\n", conf.soundDeviceOut, conf.soundDeviceIn, conf.samplerate); m_devices = fetchDevices(); printDevices(m_devices); /* Abort here if devices found are zero. */ if (m_devices.size() == 0) { closeDevice(); return 0; } RtAudio::StreamParameters outParams; RtAudio::StreamParameters inParams; outParams.deviceId = conf.soundDeviceOut == G_DEFAULT_SOUNDDEV_OUT ? m_rtAudio->getDefaultOutputDevice() : conf.soundDeviceOut; outParams.nChannels = conf.channelsOutCount; outParams.firstChannel = 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.soundDeviceIn != -1) { inParams.deviceId = conf.soundDeviceIn; inParams.nChannels = conf.channelsInCount; inParams.firstChannel = conf.channelsInStart; m_inputEnabled = true; } else m_inputEnabled = false; RtAudio::StreamOptions options; options.streamName = G_APP_NAME; options.numberOfBuffers = 4; // TODO - wtf? m_realBufferSize = conf.buffersize; m_realSampleRate = conf.samplerate; m_channelsOutCount = conf.channelsOutCount; m_channelsInCount = conf.channelsInCount; #ifdef WITH_AUDIO_JACK /* If JACK, use its own sample rate, not the one coming from the conf object. */ if (m_api == G_SYS_API_JACK) { assert(m_devices.size() > 0); assert(m_devices[0].sampleRates.size() > 0); m_realSampleRate = m_devices[0].sampleRates[0]; u::log::print("[KA] JACK in use, samplerate=%d\n", m_realSampleRate); } #endif m_callbackInfo = { /* kernelAudio = */ this, /* ready = */ true, /* withJack = */ getAPI() == G_SYS_API_JACK, /* outBuf = */ nullptr, // filled later on in audio callback /* inBuf = */ nullptr, // filled later on in audio callback /* bufferSize = */ 0, // filled later on in audio callback /* channelsOutCount = */ m_channelsOutCount, /* channelsInCount = */ m_channelsInCount}; RtAudioErrorType res = m_rtAudio->openStream( &outParams, // output params conf.soundDeviceIn != -1 ? &inParams : nullptr, // input params if inDevice is selected RTAUDIO_FLOAT32, // audio format m_realSampleRate, // sample rate &m_realBufferSize, // buffer size in byte &audioCallback, // audio callback &m_callbackInfo, // user data passed to callback &options); if (res == RtAudioErrorType::RTAUDIO_NO_ERROR) { m_ready = true; return 1; } else { closeDevice(); return 0; } } /* -------------------------------------------------------------------------- */ int KernelAudio::startStream() { if (m_rtAudio->startStream() == RtAudioErrorType::RTAUDIO_NO_ERROR) { u::log::print("[KA] Start stream - latency = %lu\n", m_rtAudio->getStreamLatency()); return 1; } return 0; } /* -------------------------------------------------------------------------- */ int KernelAudio::stopStream() { if (m_rtAudio->stopStream() == RtAudioErrorType::RTAUDIO_NO_ERROR) { u::log::print("[KA] Stop stream\n"); return 1; } return 0; } /* -------------------------------------------------------------------------- */ void KernelAudio::closeDevice() { if (!m_rtAudio->isStreamOpen()) return; m_rtAudio->stopStream(); m_rtAudio->closeStream(); m_rtAudio.reset(nullptr); } /* -------------------------------------------------------------------------- */ bool KernelAudio::isReady() const { return m_ready; } /* -------------------------------------------------------------------------- */ int KernelAudio::getBufferSize() const { return static_cast(m_realBufferSize); } int KernelAudio::getSampleRate() const { return m_realSampleRate; } int KernelAudio::getChannelsOutCount() const { return m_channelsOutCount; } int KernelAudio::getChannelsInCount() const { return m_channelsInCount; } bool KernelAudio::isInputEnabled() const { return m_inputEnabled; } /* -------------------------------------------------------------------------- */ m::KernelAudio::Device KernelAudio::getDevice(const char* name) const { for (Device device : m_devices) if (name == device.name) return device; return {0, false}; } /* -------------------------------------------------------------------------- */ const std::vector& KernelAudio::getDevices() const { return m_devices; } /* -------------------------------------------------------------------------- */ #ifdef WITH_AUDIO_JACK jack_client_t* KernelAudio::getJackHandle() const { return static_cast(m_rtAudio->HACK__getJackClient()); } #endif /* -------------------------------------------------------------------------- */ bool KernelAudio::hasAPI(int API) const { std::vector APIs; RtAudio::getCompiledApi(APIs); for (unsigned i = 0; i < APIs.size(); i++) if (APIs.at(i) == API) return true; return false; } int KernelAudio::getAPI() const { return m_api; } /* -------------------------------------------------------------------------- */ void KernelAudio::logCompiledAPIs() { std::vector APIs; RtAudio::getCompiledApi(APIs); u::log::print("[KA] Compiled RtAudio APIs: %d\n", APIs.size()); for (const RtAudio::Api& m_api : APIs) { switch (m_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; } } } /* -------------------------------------------------------------------------- */ m::KernelAudio::Device KernelAudio::fetchDevice(size_t deviceIndex) const { RtAudio::DeviceInfo info = m_rtAudio->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(info.outputChannels), static_cast(info.inputChannels), static_cast(info.duplexChannels), info.isDefaultOutput, info.isDefaultInput, u::vector::cast(info.sampleRates)}; } /* -------------------------------------------------------------------------- */ std::vector KernelAudio::fetchDevices() const { std::vector out; for (unsigned i = 0; i < m_rtAudio->getDeviceCount(); i++) out.push_back(fetchDevice(i)); return out; } /* -------------------------------------------------------------------------- */ void KernelAudio::printDevices(const std::vector& devices) const { u::log::print("[KA] %d device(s) found\n", devices.size()); for (const m::KernelAudio::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"); } } /* -------------------------------------------------------------------------- */ int KernelAudio::audioCallback(void* outBuf, void* inBuf, unsigned bufferSize, double /*streamTime*/, RtAudioStreamStatus /*status*/, void* data) { CallbackInfo info = *static_cast(data); info.outBuf = outBuf; info.inBuf = inBuf; info.bufferSize = bufferSize; return info.kernelAudio->onAudioCallback(info); } } // namespace giada::m giada-0.22.0/src/core/kernelAudio.h000066400000000000000000000074011425106661500170020ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_KERNELAUDIO_H #define G_KERNELAUDIO_H #include "core/conf.h" #include "deps/rtaudio/RtAudio.h" #include #include #include #include #include #ifdef WITH_AUDIO_JACK #include "core/jackTransport.h" #endif namespace giada::m { class KernelAudio final { public: 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 sampleRates = {}; }; struct CallbackInfo { KernelAudio* kernelAudio; bool ready; bool withJack; void* outBuf; void* inBuf; int bufferSize; int channelsOutCount; int channelsInCount; }; KernelAudio(); static void logCompiledAPIs(); int openDevice(const Conf::Data& conf); void closeDevice(); int startStream(); int stopStream(); bool isReady() const; bool isInputEnabled() const; int getBufferSize() const; int getSampleRate() const; int getChannelsOutCount() const; int getChannelsInCount() const; bool hasAPI(int API) const; int getAPI() const; Device getDevice(const char* name) const; const std::vector& getDevices() const; #ifdef WITH_AUDIO_JACK jack_client_t* getJackHandle() const; #endif /* onAudioCallback Main callback invoked on each audio block. */ std::function onAudioCallback; private: static int audioCallback(void*, void*, unsigned, double, RtAudioStreamStatus, void*); Device fetchDevice(size_t deviceIndex) const; std::vector fetchDevices() const; void printDevices(const std::vector& devices) const; #ifdef WITH_AUDIO_JACK JackTransport m_jackTransport; #endif std::vector m_devices; std::unique_ptr m_rtAudio; CallbackInfo m_callbackInfo; bool m_ready; bool m_inputEnabled; unsigned m_realBufferSize; // Real buffer size from the soundcard int m_realSampleRate; // Sample rate might differ if JACK in use int m_channelsOutCount; int m_channelsInCount; int m_api; }; } // namespace giada::m #endif giada-0.22.0/src/core/kernelMidi.cpp000066400000000000000000000174741425106661500171710ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/kernelMidi.h" #include "core/const.h" #include "utils/log.h" #include #include namespace giada::m { namespace { constexpr auto OUTPUT_NAME = "Giada MIDI output"; constexpr auto INPUT_NAME = "Giada MIDI input"; /* -------------------------------------------------------------------------- */ std::vector split_(uint32_t iValue) { return { static_cast((iValue >> 24) & 0xFF), static_cast((iValue >> 16) & 0xFF), static_cast((iValue >> 8) & 0xFF)}; } /* -------------------------------------------------------------------------- */ uint32_t join_(int b1, int b2, int b3) { return (b1 << 24) | (b2 << 16) | (b3 << 8) | (0x00); } } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ KernelMidi::KernelMidi() : onMidiReceived(nullptr) { } /* -------------------------------------------------------------------------- */ bool KernelMidi::openOutDevice(int api, int port) { if (port == -1) return false; u::log::print("[KM] Opening output device '%s', port=%d\n", OUTPUT_NAME, port); m_midiOut = makeDevice(api, OUTPUT_NAME); if (m_midiOut == nullptr) return false; return openPort(*m_midiOut, port); } /* -------------------------------------------------------------------------- */ bool KernelMidi::openInDevice(int api, int port) { if (port == -1) return false; u::log::print("[KM] Opening input device '%s', port=%d\n", INPUT_NAME, port); m_midiIn = makeDevice(api, INPUT_NAME); if (m_midiIn == nullptr) return false; if (!openPort(*m_midiIn, port)) return false; m_midiIn->setCallback(&s_callback, this); m_midiIn->ignoreTypes(true, false, true); // Ignore all system/time msgs, for now return true; } /* -------------------------------------------------------------------------- */ void KernelMidi::logPorts() { if (m_midiOut != nullptr) logPorts(*m_midiOut, OUTPUT_NAME); if (m_midiIn != nullptr) logPorts(*m_midiIn, INPUT_NAME); } /* -------------------------------------------------------------------------- */ bool KernelMidi::hasAPI(int API) const { std::vector APIs; RtMidi::getCompiledApi(APIs); for (unsigned i = 0; i < APIs.size(); i++) if (APIs.at(i) == API) return true; return false; } /* -------------------------------------------------------------------------- */ std::string KernelMidi::getOutPortName(unsigned p) const { return getPortName(*m_midiOut, p); } std::string KernelMidi::getInPortName(unsigned p) const { return getPortName(*m_midiIn, p); } /* -------------------------------------------------------------------------- */ void KernelMidi::send(uint32_t data) { if (m_midiOut == nullptr) return; std::vector msg = split_(data); m_midiOut->sendMessage(&msg); u::log::print("[KM::send] send msg=0x%X (%X %X %X)\n", data, msg[0], msg[1], msg[2]); } /* -------------------------------------------------------------------------- */ void KernelMidi::send(int b1, int b2, int b3) { if (m_midiOut == nullptr) return; std::vector msg(1, b1); if (b2 != -1) msg.push_back(b2); if (b3 != -1) msg.push_back(b3); m_midiOut->sendMessage(&msg); u::log::print("[KM::send] send msg=(%X %X %X)\n", b1, b2, b3); } /* -------------------------------------------------------------------------- */ unsigned KernelMidi::countOutPorts() const { return m_midiOut != nullptr ? m_midiOut->getPortCount() : 0; } unsigned KernelMidi::countInPorts() const { return m_midiIn != nullptr ? m_midiIn->getPortCount() : 0; } /* -------------------------------------------------------------------------- */ void KernelMidi::s_callback(double /*t*/, std::vector* msg, void* data) { static_cast(data)->callback(msg); } /* -------------------------------------------------------------------------- */ void KernelMidi::callback(std::vector* msg) { assert(onMidiReceived != nullptr); if (msg->size() < 3) { G_DEBUG("Received unknown MIDI signal - bytes=" << msg->size()); return; } onMidiReceived(join_(msg->at(0), msg->at(1), msg->at(2))); } /* -------------------------------------------------------------------------- */ template std::unique_ptr KernelMidi::makeDevice(int api, std::string name) const { try { return std::make_unique(static_cast(api), name); } catch (RtMidiError& error) { u::log::print("[KM] Error opening device '%s': %s\n", name.c_str(), error.getMessage()); return nullptr; } } template std::unique_ptr KernelMidi::makeDevice(int, std::string) const; template std::unique_ptr KernelMidi::makeDevice(int, std::string) const; /* -------------------------------------------------------------------------- */ bool KernelMidi::openPort(RtMidi& device, int port) { try { device.openPort(port, device.getPortName(port)); return true; } catch (RtMidiError& error) { u::log::print("[KM] Error opening port %d: %s\n", port, error.getMessage()); return false; } } /* -------------------------------------------------------------------------- */ std::string KernelMidi::getPortName(RtMidi& device, int port) const { try { return device.getPortName(port); } catch (RtMidiError& /*error*/) { return ""; } } /* -------------------------------------------------------------------------- */ void KernelMidi::logPorts(RtMidi& device, std::string name) const { u::log::print("[KM] Device '%s': %d MIDI ports found\n", name.c_str(), device.getPortCount()); for (unsigned i = 0; i < device.getPortCount(); i++) u::log::print(" %d) %s\n", i, device.getPortName(i)); } /* -------------------------------------------------------------------------- */ void KernelMidi::logCompiledAPIs() { std::vector apis; RtMidi::getCompiledApi(apis); u::log::print("[KM] Compiled RtMidi APIs: %d\n", apis.size()); for (const RtMidi::Api& api : apis) { switch (api) { case RtMidi::Api::UNSPECIFIED: u::log::print(" UNSPECIFIED\n"); break; case RtMidi::Api::MACOSX_CORE: u::log::print(" CoreAudio\n"); break; case RtMidi::Api::LINUX_ALSA: u::log::print(" ALSA\n"); break; case RtMidi::Api::UNIX_JACK: u::log::print(" JACK\n"); break; case RtMidi::Api::WINDOWS_MM: u::log::print(" Microsoft Multimedia MIDI API\n"); break; case RtMidi::Api::RTMIDI_DUMMY: u::log::print(" Dummy\n"); break; case RtMidi::Api::WEB_MIDI_API: u::log::print(" Web MIDI API\n"); break; default: u::log::print(" (unknown)\n"); break; } } } } // namespace giada::m giada-0.22.0/src/core/kernelMidi.h000066400000000000000000000047521425106661500166310ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_KERNELMIDI_H #define G_KERNELMIDI_H #include "midiMapper.h" #include #include #include #include #include namespace giada::m { class KernelMidi final { public: KernelMidi(); static void logCompiledAPIs(); unsigned countOutPorts() const; unsigned countInPorts() const; /* getOut/InPortName Returns the name of the port 'p'. */ std::string getOutPortName(unsigned p) const; std::string getInPortName(unsigned p) const; bool hasAPI(int API) const; /* send 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); /* setApi Sets the Api in use for both in & out messages. */ void setApi(int api); bool openOutDevice(int api, int port); bool openInDevice(int api, int port); void logPorts(); std::function onMidiReceived; private: static void s_callback(double, std::vector*, void*); void callback(std::vector*); template std::unique_ptr makeDevice(int api, std::string name) const; std::string getPortName(RtMidi&, int port) const; void logPorts(RtMidi&, std::string name) const; bool openPort(RtMidi&, int port); std::unique_ptr m_midiOut; std::unique_ptr m_midiIn; }; } // namespace giada::m #endif giada-0.22.0/src/core/metronome.cpp000066400000000000000000000034451425106661500171040ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "metronome.h" #include "deps/mcl-audio-buffer/src/audioBuffer.hpp" namespace giada::m { void Metronome::trigger(Click c, Frame o) { m_rendering = true; m_click = c; m_offset = o; } /* -------------------------------------------------------------------------- */ void Metronome::render(mcl::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::mgiada-0.22.0/src/core/metronome.h000066400000000000000000000051351425106661500165470ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_METRONOME_H #define G_METRONOME_H #include "core/types.h" namespace mcl { class AudioBuffer; } namespace giada::m { class Metronome { public: enum class Click { BEAT, BAR }; void render(mcl::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 giada-0.22.0/src/core/midiDispatcher.cpp000066400000000000000000000317121425106661500200260ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/midiDispatcher.h" #include "core/conf.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/recorder.h" #include "core/types.h" #include "glue/events.h" #include "glue/plugin.h" #include "utils/log.h" #include "utils/math.h" #include #include #include namespace giada::m { MidiDispatcher::MidiDispatcher(model::Model& m) : onDispatch(nullptr) , m_learnCb(nullptr) , m_model(m) { } /* -------------------------------------------------------------------------- */ void MidiDispatcher::startChannelLearn(int param, ID channelId, std::function f) { m_learnCb = [=](m::MidiEvent e) { learnChannel(e, param, channelId, f); }; } void MidiDispatcher::startMasterLearn(int param, std::function f) { m_learnCb = [=](m::MidiEvent e) { learnMaster(e, param, f); }; } #ifdef WITH_VST void MidiDispatcher::startPluginLearn(std::size_t paramIndex, ID pluginId, std::function f) { m_learnCb = [=](m::MidiEvent e) { learnPlugin(e, paramIndex, pluginId, f); }; } #endif void MidiDispatcher::stopLearn() { m_learnCb = nullptr; } /* -------------------------------------------------------------------------- */ void MidiDispatcher::clearMasterLearn(int param, std::function f) { learnMaster(MidiEvent(), param, f); // Empty event (0x0) } void MidiDispatcher::clearChannelLearn(int param, ID channelId, std::function f) { learnChannel(MidiEvent(), param, channelId, f); // Empty event (0x0) } #ifdef WITH_VST void MidiDispatcher::clearPluginLearn(std::size_t paramIndex, ID pluginId, std::function f) { learnPlugin(MidiEvent(), paramIndex, pluginId, f); // Empty event (0x0) } #endif /* -------------------------------------------------------------------------- */ void MidiDispatcher::dispatch(uint32_t msg) { assert(onDispatch != nullptr); /* Here we want to catch two things: a) note on/note off from a MIDI keyboard and b) knob/wheel/slider movements from a MIDI controller. We must also fix the velocity zero issue for those devices that sends NOTE OFF events as NOTE ON + velocity zero. Let's make it a real NOTE OFF event. */ MidiEvent midiEvent(msg); midiEvent.fixVelocityZero(); u::log::print("[midiDispatcher] MIDI received - 0x%X (chan %d)\n", midiEvent.getRaw(), midiEvent.getChannel()); /* Start dispatcher. Don't parse channels if MIDI learn is ON, just learn the incoming MIDI signal. The action is not invoked directly, but scheduled to be performed by the Event Dispatcher. */ Action action = {0, 0, 0, midiEvent}; auto event = m_learnCb != nullptr ? EventDispatcher::EventType::MIDI_DISPATCHER_LEARN : EventDispatcher::EventType::MIDI_DISPATCHER_PROCESS; onDispatch(event, action); } /* -------------------------------------------------------------------------- */ void MidiDispatcher::learn(const MidiEvent& e) { assert(m_learnCb != nullptr); m_learnCb(e); } /* -------------------------------------------------------------------------- */ void MidiDispatcher::process(const MidiEvent& e) { assert(onEventReceived != nullptr); processMaster(e); processChannels(e); onEventReceived(); } /* -------------------------------------------------------------------------- */ bool MidiDispatcher::isMasterMidiInAllowed(int c) { int filter = m_model.get().midiIn.filter; bool enabled = m_model.get().midiIn.enabled; return enabled && (filter == -1 || filter == c); } /* -------------------------------------------------------------------------- */ bool MidiDispatcher::isChannelMidiInAllowed(ID channelId, int c) { return m_model.get().getChannel(channelId).midiLearner.isAllowed(c); } /* -------------------------------------------------------------------------- */ #ifdef WITH_VST void MidiDispatcher::processPlugins(ID channelId, const std::vector& plugins, const MidiEvent& midiEvent) { uint32_t pure = midiEvent.getRawNoVelocity(); float vf = u::math::map(midiEvent.getVelocity(), G_MAX_VELOCITY, 1.0f); /* Plugins' parameters layout reflects the structure of the matrix Channel::midiInPlugins. It is safe to assume then that Plugin 'p' and parameter indexes match both the structure of Channel::midiInPlugins and the vector of plugins. */ for (Plugin* p : plugins) { for (const MidiLearnParam& param : p->midiInParams) { if (pure != param.getValue()) continue; c::events::setPluginParameter(channelId, p->id, param.getIndex(), vf, Thread::MIDI); u::log::print(" >>> [pluginId=%d paramIndex=%d] (pure=0x%X, value=%d, float=%f)\n", p->id, param.getIndex(), pure, midiEvent.getVelocity(), vf); } } } #endif /* -------------------------------------------------------------------------- */ void MidiDispatcher::processChannels(const MidiEvent& midiEvent) { uint32_t pure = midiEvent.getRawNoVelocity(); for (const Channel& c : m_model.get().channels) { /* Do nothing on this channel if MIDI in is disabled or filtered out for the current MIDI channel. */ if (!c.midiLearner.isAllowed(midiEvent.getChannel())) continue; 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.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.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.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.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.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); } 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.id, c.plugins, midiEvent); #endif /* Redirect raw MIDI message (pure + velocity) to plug-ins in armed channels. */ if (c.armed) c::events::sendMidiToChannel(c.id, midiEvent, Thread::MIDI); } } /* -------------------------------------------------------------------------- */ void MidiDispatcher::processMaster(const MidiEvent& midiEvent) { const uint32_t pure = midiEvent.getRawNoVelocity(); const model::MidiIn& midiIn = m_model.get().midiIn; if (pure == midiIn.rewind) { c::events::rewindSequencer(Thread::MIDI); u::log::print(" >>> rewind (master) (pure=0x%X)\n", pure); } 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) { c::events::toggleActionRecording(); u::log::print(" >>> actionRec (master) (pure=0x%X)\n", pure); } else if (pure == midiIn.inputRec) { c::events::toggleInputRecording(); u::log::print(" >>> inputRec (master) (pure=0x%X)\n", pure); } 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); c::events::setMasterInVolume(vf, Thread::MIDI); u::log::print(" >>> input volume (master) (pure=0x%X, value=%d, float=%f)\n", pure, midiEvent.getVelocity(), vf); } 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); } else if (pure == midiIn.beatDouble) { c::events::multiplyBeats(); u::log::print(" >>> sequencer x2 (master) (pure=0x%X)\n", pure); } else if (pure == midiIn.beatHalf) { c::events::divideBeats(); u::log::print(" >>> sequencer /2 (master) (pure=0x%X)\n", pure); } } /* -------------------------------------------------------------------------- */ void MidiDispatcher::learnChannel(MidiEvent e, int param, ID channelId, std::function doneCb) { if (!isChannelMidiInAllowed(channelId, e.getChannel())) return; uint32_t raw = e.getRawNoVelocity(); Channel& ch = m_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; } m_model.swap(model::SwapType::SOFT); stopLearn(); doneCb(); } /* -------------------------------------------------------------------------- */ void MidiDispatcher::learnMaster(MidiEvent e, int param, std::function doneCb) { if (!isMasterMidiInAllowed(e.getChannel())) return; uint32_t raw = e.getRawNoVelocity(); switch (param) { case G_MIDI_IN_REWIND: m_model.get().midiIn.rewind = raw; break; case G_MIDI_IN_START_STOP: m_model.get().midiIn.startStop = raw; break; case G_MIDI_IN_ACTION_REC: m_model.get().midiIn.actionRec = raw; break; case G_MIDI_IN_INPUT_REC: m_model.get().midiIn.inputRec = raw; break; case G_MIDI_IN_METRONOME: m_model.get().midiIn.metronome = raw; break; case G_MIDI_IN_VOLUME_IN: m_model.get().midiIn.volumeIn = raw; break; case G_MIDI_IN_VOLUME_OUT: m_model.get().midiIn.volumeOut = raw; break; case G_MIDI_IN_BEAT_DOUBLE: m_model.get().midiIn.beatDouble = raw; break; case G_MIDI_IN_BEAT_HALF: m_model.get().midiIn.beatHalf = raw; break; } m_model.swap(model::SwapType::SOFT); stopLearn(); doneCb(); } /* -------------------------------------------------------------------------- */ #ifdef WITH_VST void MidiDispatcher::learnPlugin(MidiEvent e, std::size_t paramIndex, ID pluginId, std::function doneCb) { model::DataLock lock = m_model.lockData(model::SwapType::NONE); Plugin* plugin = m_model.findShared(pluginId); assert(plugin != nullptr); assert(paramIndex < plugin->midiInParams.size()); plugin->midiInParams[paramIndex].setValue(e.getRawNoVelocity()); stopLearn(); doneCb(); } #endif } // namespace giada::m giada-0.22.0/src/core/midiDispatcher.h000066400000000000000000000066461425106661500175030ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_MIDI_DISPATCHER_H #define G_MIDI_DISPATCHER_H #include "core/actions/action.h" #include "core/midiEvent.h" #include "core/model/model.h" #include "core/types.h" #include #include #include namespace giada::m { class MidiDispatcher { public: MidiDispatcher(model::Model&); void startChannelLearn(int param, ID channelId, std::function f); void startMasterLearn(int param, std::function f); void stopLearn(); void clearMasterLearn(int param, std::function f); void clearChannelLearn(int param, ID channelId, std::function f); #ifdef WITH_VST void startPluginLearn(std::size_t paramIndex, ID pluginId, std::function f); void clearPluginLearn(std::size_t paramIndex, ID pluginId, std::function f); #endif /* dispatch Main callback invoked by kernelMidi whenever a new MIDI data comes in. */ void dispatch(uint32_t msg); /* learn Learns event 'e'. Called by the Event Dispatcher. */ void learn(const MidiEvent& e); /* process Sends event 'e' to channels (masters and keyboard). Called by the Event Dispatcher. */ void process(const MidiEvent& e); /* onDispatch Callback fired when the dispatch() method is invoked by KernelMidi. */ std::function onDispatch; /* onEventReceived Callback fired when a MIDI event has been received (passed in by the Event Dispatcher). */ std::function onEventReceived; private: bool isMasterMidiInAllowed(int c); bool isChannelMidiInAllowed(ID channelId, int c); void processChannels(const MidiEvent& midiEvent); void processMaster(const MidiEvent& midiEvent); void learnChannel(MidiEvent e, int param, ID channelId, std::function doneCb); void learnMaster(MidiEvent e, int param, std::function doneCb); #ifdef WITH_VST void processPlugins(ID channelId, const std::vector& plugins, const MidiEvent& midiEvent); void learnPlugin(MidiEvent e, std::size_t paramIndex, ID pluginId, std::function doneCb); #endif /* cb_midiLearn Callback prepared by the gdMidiGrabber window and called by midiDispatcher. It contains things to do once the midi message has been stored. */ std::function m_learnCb; model::Model& m_model; }; } // namespace giada::m #endif giada-0.22.0/src/core/midiEvent.cpp000066400000000000000000000074101425106661500170170ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "midiEvent.h" #include "const.h" #include "utils/math.h" #include namespace giada { namespace m { namespace { constexpr int FLOAT_FACTOR = 10000; } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ MidiEvent::MidiEvent(uint32_t raw, int delta) : m_status((raw & 0xF0000000) >> 24) , m_channel((raw & 0x0F000000) >> 24) , m_note((raw & 0x00FF0000) >> 16) , m_velocity((raw & 0x0000FF00) >> 8) , m_delta(delta) { } /* -------------------------------------------------------------------------- */ /* static_cast to avoid ambiguity with MidiEvent(float). */ MidiEvent::MidiEvent(int byte1, int byte2, int byte3, int delta) : MidiEvent(static_cast((byte1 << 24) | (byte2 << 16) | (byte3 << 8) | (0x00)), delta) { } /* -------------------------------------------------------------------------- */ MidiEvent::MidiEvent(float v, int delta) : MidiEvent(ENVELOPE, 0, 0, delta) { m_velocity = static_cast(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_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); } } // namespace m } // namespace giada giada-0.22.0/src/core/midiEvent.h000066400000000000000000000053241425106661500164660ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_MIDI_EVENT_H #define G_MIDI_EVENT_H #include 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; static const int ENVELOPE = 0xB0; /* MidiEvent (1) Creates and empty and invalid MIDI event. */ MidiEvent() = default; MidiEvent(uint32_t raw, int delta = 0); MidiEvent(int byte1, int byte2, int byte3, int delta = 0); /* MidiEvent (4) A constructor that takes a float parameter. Useful to build ENVELOPE events for automations, volume and pitch. */ MidiEvent(float v, int delta = 0); int getStatus() const; int getChannel() const; int getNote() const; int getVelocity() const; float getVelocityFloat() const; bool isNoteOnOff() const; int getDelta() const; /* getRaw(), getRawNoVelocity() Returns the raw MIDI message. If getRawNoVelocity(), the velocity value is stripped off (i.e. velocity == 0). */ uint32_t getRaw() const; uint32_t getRawNoVelocity() const; void setDelta(int d); void setChannel(int c); void setVelocity(int v); /* fixVelocityZero() According to the MIDI standard, there is a special case if the velocity is set to zero. The NOTE ON message then has the same meaning as a NOTE OFF message, switching the note off. Let's fix it. Sometime however you do want a NOTE ON with velocity zero: setting velocity to 0 in MIDI action editor to mute a specific event. */ void fixVelocityZero(); private: int m_status; int m_channel; int m_note; int m_velocity; int m_delta; }; } // namespace m } // namespace giada #endif giada-0.22.0/src/core/midiLearnParam.cpp000066400000000000000000000032201425106661500177530ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "midiLearnParam.h" #include namespace giada::m { MidiLearnParam::MidiLearnParam() : m_param(0) , m_index(0) { } MidiLearnParam::MidiLearnParam(uint32_t v, std::size_t index) : m_param(v) , m_index(index) { } /* -------------------------------------------------------------------------- */ uint32_t MidiLearnParam::getValue() const { return m_param.load(); } void MidiLearnParam::setValue(uint32_t v) { m_param.store(v); } std::size_t MidiLearnParam::getIndex() const { return m_index; } } // namespace giada::m giada-0.22.0/src/core/midiLearnParam.h000066400000000000000000000031611425106661500174240ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_MIDI_LEARN_PARAM_H #define G_MIDI_LEARN_PARAM_H #include "core/weakAtomic.h" #include #include namespace giada::m { class MidiLearnParam { public: MidiLearnParam(); MidiLearnParam(uint32_t v, std::size_t index = 0); MidiLearnParam(const MidiLearnParam& o) = default; uint32_t getValue() const; std::size_t getIndex() const; void setValue(uint32_t v); private: WeakAtomic m_param; std::size_t m_index; }; } // namespace giada::m #endif giada-0.22.0/src/core/midiMapper.cpp000066400000000000000000000161211425106661500171610ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/midiMapper.h" #include "core/const.h" #include "core/kernelMidi.h" #include "core/midiEvent.h" #include "utils/fs.h" #include "utils/log.h" #include "utils/string.h" #include #include #include #include #include #include #ifdef WITH_TESTS #include "tests/mocks/kernelMidiMock.h" #endif namespace nl = nlohmann; namespace giada::m { bool MidiMap::isValid() const { return !(brand.empty() || device.empty()); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ template MidiMapper::MidiMapper(KernelMidiI& k) : m_kernelMidi(k) { m_mapsPath = u::fs::getMidiMapsPath(); } /* -------------------------------------------------------------------------- */ template void MidiMapper::init() { Mapper::init(); u::log::print("[MidiMapper::init] total midimaps found: %d\n", m_mapFiles.size()); } /* -------------------------------------------------------------------------- */ template int MidiMapper::read(const std::string& file) { std::optional res = Mapper::read(file); if (!res) return G_FILE_UNREADABLE; nl::json j = res.value(); currentMap.brand = j[MIDIMAP_KEY_BRAND]; currentMap.device = j[MIDIMAP_KEY_DEVICE]; if (!readInitCommands(currentMap, j)) return G_FILE_UNREADABLE; if (readCommand(j, currentMap.muteOn, MIDIMAP_KEY_MUTE_ON)) parse(currentMap.muteOn); if (readCommand(j, currentMap.muteOff, MIDIMAP_KEY_MUTE_OFF)) parse(currentMap.muteOff); if (readCommand(j, currentMap.soloOn, MIDIMAP_KEY_SOLO_ON)) parse(currentMap.soloOn); if (readCommand(j, currentMap.soloOff, MIDIMAP_KEY_SOLO_OFF)) parse(currentMap.soloOff); if (readCommand(j, currentMap.waiting, MIDIMAP_KEY_WAITING)) parse(currentMap.waiting); if (readCommand(j, currentMap.playing, MIDIMAP_KEY_PLAYING)) parse(currentMap.playing); if (readCommand(j, currentMap.stopping, MIDIMAP_KEY_STOPPING)) parse(currentMap.stopping); if (readCommand(j, currentMap.stopped, MIDIMAP_KEY_STOPPED)) parse(currentMap.stopped); if (readCommand(j, currentMap.playingInaudible, MIDIMAP_KEY_PLAYING_INAUDIBLE)) parse(currentMap.playingInaudible); return G_FILE_OK; } /* -------------------------------------------------------------------------- */ template bool MidiMapper::isMessageDefined(const MidiMap::Message& m) const { return m.offset != -1; } /* -------------------------------------------------------------------------- */ template void MidiMapper::sendInitMessages(const MidiMap& midiMap) { if (!midiMap.isValid()) return; for (const MidiMap::Message& m : midiMap.initCommands) { if (m.value == 0x0 || m.channel == -1) continue; MidiEvent e(m.value); e.setChannel(m.channel); m_kernelMidi.send(e.getRaw()); } } /* -------------------------------------------------------------------------- */ template void MidiMapper::sendMidiLightning(uint32_t learnt, const MidiMap::Message& m) { // Skip lightning message if not defined in midi map if (!isMessageDefined(m)) { u::log::print("[MidiMapper::sendMidiLightning] message skipped (not defined in midiMap)\n"); return; } u::log::print("[MidiMapper::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. */ uint32_t out = ((learnt & 0x00FF0000) >> 16) << m.offset; /* Merge the previously prepared channel into final message, and finally send it. */ out |= m.value | (m.channel << 24); m_kernelMidi.send(out); } /* -------------------------------------------------------------------------- */ template void MidiMapper::parse(MidiMap::Message& message) const { /* Remove '0x' part from the original string. */ std::string input = message.valueStr; std::size_t f = input.find("0x"); // check if "0x" is there if (f != std::string::npos) input = message.valueStr.replace(f, 2, ""); /* Then transform string value into the actual uint32_t value, by parsing each char (i.e. nibble) in the original string. Substitute 'n' with zeros. */ std::string output; for (unsigned i = 0, p = 24; i < input.length(); i++, p -= 4) { if (input[i] == 'n') { output += '0'; if (message.offset == -1) // do it once message.offset = p; } else output += input[i]; } /* From string to uint32_t */ message.value = strtoul(output.c_str(), nullptr, 16); u::log::print("[MidiMapper::parse] parsed chan=%d valueStr=%s value=%#x, offset=%d\n", message.channel, message.valueStr, message.value, message.offset); } /* -------------------------------------------------------------------------- */ template bool MidiMapper::readCommand(const nl::json& j, MidiMap::Message& m, const std::string& key) const { if (j.find(key) == j.end()) return false; const nl::json& jc = j[key]; m.channel = jc[MIDIMAP_KEY_CHANNEL]; m.valueStr = jc[MIDIMAP_KEY_MESSAGE]; return true; } /* -------------------------------------------------------------------------- */ template bool MidiMapper::readInitCommands(MidiMap& midiMap, const nl::json& j) { if (j.find(MIDIMAP_KEY_INIT_COMMANDS) == j.end()) return false; for (const auto& jc : j[MIDIMAP_KEY_INIT_COMMANDS]) { MidiMap::Message m; m.channel = jc[MIDIMAP_KEY_CHANNEL]; m.valueStr = jc[MIDIMAP_KEY_MESSAGE]; m.value = strtoul(m.valueStr.c_str(), nullptr, 16); midiMap.initCommands.push_back(m); } return true; } template class MidiMapper; #ifdef WITH_TESTS template class MidiMapper; #endif } // namespace giada::m giada-0.22.0/src/core/midiMapper.h000066400000000000000000000071121425106661500166260ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_MIDIMAPPER_H #define G_MIDIMAPPER_H #include "mapper.h" #include #include #include namespace giada::m { class KernelMidi; #ifdef WITH_TESTS class KernelMidiMock; #endif } // namespace giada::m namespace giada::m { struct MidiMap { struct Message { int channel = 0; std::string valueStr = ""; int offset = -1; uint32_t value = 0; }; /* isValid A valid MidiMap must have the brand and the device defined. */ bool isValid() const; std::string brand; std::string device; std::vector initCommands; Message muteOn; Message muteOff; Message soloOn; Message soloOff; Message waiting; Message playing; Message stopping; Message stopped; Message playingInaudible; }; /* -------------------------------------------------------------------------- */ template class MidiMapper final : public Mapper { public: MidiMapper(KernelMidiI&); /* init Parses the midimap folders and find the available midimaps. */ void init(); /* read Reads a midimap from file 'file' and sets it as the current one. */ int read(const std::string& file); /* sendInitMessages Sends initialization messages from the midimap to the connected MIDI devices. */ void sendInitMessages(const MidiMap& midiMap); /* sendMidiLightning Sends a MIDI lightning message defined by 'msg'. */ void sendMidiLightning(uint32_t learnt, const MidiMap::Message& msg); /* currentMap The current MidiMap selected and loaded. It might be invalid if no midimaps have been found. */ MidiMap currentMap; private: KernelMidiI& m_kernelMidi; /* isMessageDefined Checks whether a specific message has been defined within a midimap file. */ bool isMessageDefined(const MidiMap::Message& m) const; /* parse Takes a string message with 'nn' in it and turns it into a real MIDI value. TODO - don't edit message in place! */ void parse(MidiMap::Message& message) const; /* TODO - don't edit midiMap in place! */ bool readInitCommands(MidiMap& midiMap, const nlohmann::json& j); /* TODO - don't edit message in place! */ bool readCommand(const nlohmann::json& j, MidiMap::Message& m, const std::string& key) const; }; extern template class MidiMapper; #ifdef WITH_TESTS extern template class MidiMapper; #endif } // namespace giada::m #endif giada-0.22.0/src/core/mixer.cpp000066400000000000000000000237711425106661500162270ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/mixer.h" #include "core/const.h" #include "core/model/model.h" #include "utils/log.h" #include "utils/math.h" namespace giada::m { namespace { /* CH_LEFT, CH_RIGHT Channels identifiers. */ constexpr int CH_LEFT = 0; constexpr int CH_RIGHT = 1; } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ Mixer::Mixer(model::Model& m) : onSignalTresholdReached(nullptr) , onEndOfRecording(nullptr) , m_model(m) , m_signalCbFired(false) , m_endOfRecCbFired(false) { } /* -------------------------------------------------------------------------- */ void Mixer::reset(Frame maxFramesInLoop, Frame framesInBuffer) { /* Allocate working buffers. rec buffer has variable size: it depends on how many frames there are in the current loop. */ m_model.get().mixer.getRecBuffer().alloc(maxFramesInLoop, G_MAX_IO_CHANS); m_model.get().mixer.getInBuffer().alloc(framesInBuffer, G_MAX_IO_CHANS); u::log::print("[mixer::reset] buffers ready - maxFramesInLoop=%d, framesInBuffer=%d\n", maxFramesInLoop, framesInBuffer); } /* -------------------------------------------------------------------------- */ bool Mixer::isActive() const { return m_model.get().mixer.a_isActive(); } /* -------------------------------------------------------------------------- */ void Mixer::enable() { m_model.get().mixer.a_setActive(true); u::log::print("[mixer::enable] enabled\n"); } void Mixer::disable() { m_model.get().mixer.a_setActive(false); while (m_model.isLocked()) ; u::log::print("[mixer::disable] disabled\n"); } /* -------------------------------------------------------------------------- */ void Mixer::allocRecBuffer(Frame frames) { m_model.get().mixer.getRecBuffer().alloc(frames, G_MAX_IO_CHANS); } void Mixer::clearRecBuffer() { m_model.get().mixer.getRecBuffer().clear(); } const mcl::AudioBuffer& Mixer::getRecBuffer() { return m_model.get().mixer.getRecBuffer(); } /* -------------------------------------------------------------------------- */ void Mixer::advanceChannels(const Sequencer::EventBuffer& events, const model::Layout& rtLayout, Range block, Frame quantizerStep) { for (const Channel& c : rtLayout.channels) if (!c.isInternal()) c.advance(events, block, quantizerStep); } /* -------------------------------------------------------------------------- */ void Mixer::render(mcl::AudioBuffer& out, const mcl::AudioBuffer& in, const model::Layout& layout_RT) const { const model::Mixer& mixer = layout_RT.mixer; const model::Sequencer& sequencer = layout_RT.sequencer; const model::Recorder& recorder = layout_RT.recorder; const Channel& masterOutCh = layout_RT.getChannel(Mixer::MASTER_OUT_CHANNEL_ID); const Channel& masterInCh = layout_RT.getChannel(Mixer::MASTER_IN_CHANNEL_ID); const Channel& previewCh = layout_RT.getChannel(Mixer::PREVIEW_CHANNEL_ID); const bool hasInput = in.isAllocd(); const bool inToOut = mixer.inToOut; const bool isSeqActive = sequencer.isActive(); const bool shouldLineInRec = isSeqActive && recorder.a_isRecordingInput() && hasInput; const float recTriggerLevel = mixer.recTriggerLevel; const Frame maxFramesToRec = mixer.maxFramesToRec; const bool allowsOverdub = mixer.allowsOverdub; const bool limitOutput = mixer.limitOutput; mixer.getInBuffer().clear(); /* Reset peak computation. */ mixer.a_setPeakOut({0.0f, 0.0f}); mixer.a_setPeakIn({0.0f, 0.0f}); if (hasInput) { processLineIn(mixer, in, masterInCh.volume, recTriggerLevel, isSeqActive); renderMasterIn(masterInCh, mixer.getInBuffer()); } if (shouldLineInRec) { const Frame newTrackerPos = lineInRec(in, mixer.getRecBuffer(), mixer.a_getInputTracker(), maxFramesToRec, masterInCh.volume, allowsOverdub); mixer.a_setInputTracker(newTrackerPos); } /* Channel processing. Don't do it if layout is locked: another thread is changing data (e.g. Plugins or Waves). */ if (!layout_RT.locked) renderChannels(layout_RT.channels, out, mixer.getInBuffer()); /* Render remaining internal channels. */ renderMasterOut(masterOutCh, out); renderPreview(previewCh, out); /* Post processing. */ finalizeOutput(mixer, out, inToOut, limitOutput, masterOutCh.volume); } /* -------------------------------------------------------------------------- */ void Mixer::startInputRec(Frame from) { m_model.get().mixer.a_setInputTracker(from); } Frame Mixer::stopInputRec() { const Frame ret = m_model.get().mixer.a_getInputTracker(); m_model.get().mixer.a_setInputTracker(0); m_signalCbFired = false; m_endOfRecCbFired = false; return ret; } /* -------------------------------------------------------------------------- */ bool Mixer::isChannelAudible(const Channel& c) const { if (c.isInternal()) return true; if (c.isMuted()) return false; const bool hasSolos = m_model.get().mixer.hasSolos; return !hasSolos || (hasSolos && c.isSoloed()); } /* -------------------------------------------------------------------------- */ Peak Mixer::getPeakOut() const { return m_model.get().mixer.a_getPeakOut(); } Peak Mixer::getPeakIn() const { return m_model.get().mixer.a_getPeakIn(); } /* -------------------------------------------------------------------------- */ Mixer::RecordInfo Mixer::getRecordInfo() const { return { m_model.get().mixer.a_getInputTracker(), m_model.get().mixer.getRecBuffer().countFrames()}; } /* -------------------------------------------------------------------------- */ bool Mixer::thresholdReached(Peak p, float threshold) const { return u::math::linearToDB(p.left) > threshold || u::math::linearToDB(p.right) > threshold; } /* -------------------------------------------------------------------------- */ Peak Mixer::makePeak(const mcl::AudioBuffer& b) const { if (!b.isAllocd()) return {0.0f, 0.0f}; return {b.getPeak(CH_LEFT), b.getPeak(b.countChannels() == 1 ? CH_LEFT : CH_RIGHT)}; } /* -------------------------------------------------------------------------- */ Frame Mixer::lineInRec(const mcl::AudioBuffer& inBuf, mcl::AudioBuffer& recBuf, Frame inputTracker, Frame maxFrames, float inVol, bool allowsOverdub) const { assert(maxFrames > 0 && maxFrames <= recBuf.countFrames()); assert(onEndOfRecording != nullptr); if (inputTracker >= maxFrames && !allowsOverdub && !m_endOfRecCbFired) { onEndOfRecording(); m_endOfRecCbFired = true; return 0; } const Frame framesToCopy = -1; // copy everything const Frame srcOffset = 0; const Frame destOffset = inputTracker % maxFrames; // loop over at maxFrames recBuf.sum(inBuf, framesToCopy, srcOffset, destOffset, inVol); return inputTracker + inBuf.countFrames(); } /* -------------------------------------------------------------------------- */ void Mixer::processLineIn(const model::Mixer& mixer, const mcl::AudioBuffer& inBuf, float inVol, float recTriggerLevel, bool isSeqActive) const { const Peak peak = makePeak(inBuf); if (thresholdReached(peak, recTriggerLevel) && !m_signalCbFired && isSeqActive) { m_signalCbFired = true; onSignalTresholdReached(); G_DEBUG("Signal > threshold!"); } mixer.a_setPeakIn(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() <= mixer.getInBuffer().countChannels()); mixer.getInBuffer().set(inBuf, inVol); } /* -------------------------------------------------------------------------- */ void Mixer::renderChannels(const std::vector& channels, mcl::AudioBuffer& out, mcl::AudioBuffer& in) const { for (const Channel& c : channels) if (!c.isInternal()) c.render(&out, &in, isChannelAudible(c)); } /* -------------------------------------------------------------------------- */ void Mixer::renderMasterIn(const Channel& ch, mcl::AudioBuffer& in) const { #ifdef WITH_VST ch.render(nullptr, &in, true); #else (void)ch; (void)in; #endif } void Mixer::renderMasterOut(const Channel& ch, mcl::AudioBuffer& out) const { ch.render(&out, nullptr, true); } void Mixer::renderPreview(const Channel& ch, mcl::AudioBuffer& out) const { ch.render(&out, nullptr, true); } /* -------------------------------------------------------------------------- */ void Mixer::limit(mcl::AudioBuffer& outBuf) const { 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)); } /* -------------------------------------------------------------------------- */ void Mixer::finalizeOutput(const model::Mixer& mixer, mcl::AudioBuffer& buf, bool inToOut, bool shouldLimit, float vol) const { if (inToOut) buf.sum(mixer.getInBuffer(), vol); else buf.applyGain(vol); if (shouldLimit) limit(buf); mixer.a_setPeakOut({buf.getPeak(CH_LEFT), buf.getPeak(CH_RIGHT)}); } } // namespace giada::m giada-0.22.0/src/core/mixer.h000066400000000000000000000134711425106661500156700ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_MIXER_H #define G_MIXER_H #include "core/midiEvent.h" #include "core/queue.h" #include "core/ringBuffer.h" #include "core/sequencer.h" #include "core/types.h" #include "core/weakAtomic.h" #include "deps/mcl-audio-buffer/src/audioBuffer.hpp" #include "src/core/actions/actions.h" #include namespace mcl { class AudioBuffer; } namespace giada::m::model { class Mixer; struct Layout; } // namespace giada::m::model namespace giada::m { struct Action; class Channel; class MixerHandler; class Mixer { public: friend MixerHandler; static constexpr int MASTER_OUT_CHANNEL_ID = 1; static constexpr int MASTER_IN_CHANNEL_ID = 2; static constexpr int PREVIEW_CHANNEL_ID = 3; /* RecordInfo Information regarding the input recording progress. */ struct RecordInfo { Frame position; Frame maxLength; }; Mixer(model::Model&); /* isActive Mixer might be inactive (not initialized or suspended). */ bool isActive() const; /* isChannelAudible True if the channel 'c' is currently audible: not muted or not included in a solo session. */ bool isChannelAudible(const Channel& c) const; Peak getPeakOut() const; Peak getPeakIn() const; /* getRecordInfo Returns information on the ongoing input recording. */ RecordInfo getRecordInfo() const; /* render Core rendering function. */ void render(mcl::AudioBuffer& out, const mcl::AudioBuffer& in, const model::Layout&) const; /* reset Brings everything back to the initial state. */ void reset(Frame framesInLoop, Frame framesInBuffer); /* enable, disable Toggles master callback processing. Useful to suspend the rendering. */ void enable(); void disable(); /* allocRecBuffer Allocates new memory for the virtual input channel. */ void allocRecBuffer(Frame frames); /* clearRecBuffer Clears internal virtual channel. */ void clearRecBuffer(); /* getRecBuffer Returns a read-only reference to the internal virtual channel. Use this to merge data into channel after an input recording session. */ const mcl::AudioBuffer& getRecBuffer(); /* advanceChannels Processes Channels' static events (e.g. pre-recorded actions or sequencer events) in the current audio block. Called by the main audio thread when the sequencer is running. */ void advanceChannels(const Sequencer::EventBuffer&, const model::Layout&, Range, Frame quantizerStep); /* onSignalTresholdReached Callback fired when audio has reached a certain threshold (record-on-signal mode). */ std::function onSignalTresholdReached; /* onEndOfRecording Callback fired when the audio recording session has ended. */ std::function onEndOfRecording; private: /* thresholdReached Returns true if left or right channel's peak has reached a certain threshold. */ bool thresholdReached(Peak p, float threshold) const; /* makePeak Returns a Peak object given an audio buffer, taking number of channels into account. */ Peak makePeak(const mcl::AudioBuffer& b) const; /* lineInRec 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. Returns the number of recorded frames. */ Frame lineInRec(const mcl::AudioBuffer& inBuf, mcl::AudioBuffer& recBuf, Frame inputTracker, Frame maxFrames, float inVol, bool allowsOverdub) const; /* processLineIn Computes line in peaks and prepares the internal working buffer for input recording. */ void processLineIn(const model::Mixer& mixer, const mcl::AudioBuffer& inBuf, float inVol, float recTriggerLevel, bool isSeqActive) const; void renderChannels(const std::vector& channels, mcl::AudioBuffer& out, mcl::AudioBuffer& in) const; void renderMasterIn(const Channel&, mcl::AudioBuffer& in) const; void renderMasterOut(const Channel&, mcl::AudioBuffer& out) const; void renderPreview(const Channel&, mcl::AudioBuffer& out) const; /* limit Applies a very dumb hard limiter. */ void limit(mcl::AudioBuffer& outBuf) const; /* finalizeOutput Last touches after the output has been rendered: apply inToOut if any, apply output volume, compute peak. */ void finalizeOutput(const model::Mixer&, mcl::AudioBuffer&, bool inToOut, bool limit, float vol) const; /* startInputRec, stopInputRec Starts/stops input recording on frame 'from'. The latter returns the number of recorded frames. */ void startInputRec(Frame from); Frame stopInputRec(); model::Model& m_model; /* m_signalCbFired, m_endOfRecCbFired Boolean guards to determine whether the callbacks have been fired or not, to avoid retriggering. Mutable: strictly for internal use only. */ mutable bool m_signalCbFired; mutable bool m_endOfRecCbFired; }; } // namespace giada::m #endif giada-0.22.0/src/core/mixerHandler.cpp000066400000000000000000000305161425106661500175200ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/mixerHandler.h" #include "core/channels/channelManager.h" #include "core/const.h" #include "core/mixer.h" #include "core/model/model.h" #include "core/plugins/pluginManager.h" #include "core/recorder.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 #include #include #include namespace giada::m { MixerHandler::MixerHandler(model::Model& model, Mixer& mixer) : onChannelsAltered(nullptr) , onChannelRecorded(nullptr) , m_model(model) , m_mixer(mixer) { } /* -------------------------------------------------------------------------- */ void MixerHandler::reset(Frame framesInLoop, Frame framesInBuffer, ChannelManager& channelManager) { m_mixer.reset(framesInLoop, framesInBuffer); m_model.get().channels.clear(); m_model.get().channels.push_back(channelManager.create( Mixer::MASTER_OUT_CHANNEL_ID, ChannelType::MASTER, /*columnId=*/0, framesInBuffer)); m_model.get().channels.push_back(channelManager.create( Mixer::MASTER_IN_CHANNEL_ID, ChannelType::MASTER, /*columnId=*/0, framesInBuffer)); m_model.get().channels.push_back(channelManager.create( Mixer::PREVIEW_CHANNEL_ID, ChannelType::PREVIEW, /*columnId=*/0, framesInBuffer)); m_model.swap(model::SwapType::NONE); } /* -------------------------------------------------------------------------- */ Channel& MixerHandler::addChannel(ChannelType type, ID columnId, int bufferSize, ChannelManager& channelManager) { m_model.get().channels.push_back(channelManager.create(/*id=*/0, type, columnId, bufferSize)); m_model.swap(model::SwapType::HARD); return m_model.get().channels.back(); } /* -------------------------------------------------------------------------- */ void MixerHandler::loadChannel(ID channelId, std::unique_ptr w) { assert(onChannelsAltered != nullptr); m_model.addShared(std::move(w)); Channel& channel = m_model.get().getChannel(channelId); Wave& wave = m_model.backShared(); Wave* old = channel.samplePlayer->getWave(); loadChannel(channel, &wave); m_model.swap(model::SwapType::HARD); /* Remove old wave, if any. It is safe to do it now: the audio thread is already processing the new layout. */ if (old != nullptr) m_model.removeShared(*old); onChannelsAltered(); } /* -------------------------------------------------------------------------- */ void MixerHandler::addAndLoadChannel(ID columnId, std::unique_ptr w, int bufferSize, ChannelManager& channelManager) { assert(onChannelsAltered != nullptr); m_model.addShared(std::move(w)); Wave& wave = m_model.backShared(); Channel& channel = addChannel(ChannelType::SAMPLE, columnId, bufferSize, channelManager); loadChannel(channel, &wave); m_model.swap(model::SwapType::HARD); onChannelsAltered(); } /* -------------------------------------------------------------------------- */ #ifdef WITH_VST void MixerHandler::cloneChannel(ID channelId, int sampleRate, int bufferSize, ChannelManager& channelManager, WaveManager& waveManager, const Sequencer& sequencer, PluginManager& pluginManager) #else void MixerHandler::cloneChannel(ID channelId, int bufferSize, ChannelManager& channelManager, WaveManager& waveManager) #endif { const Channel& oldChannel = m_model.get().getChannel(channelId); Channel newChannel = channelManager.create(oldChannel, bufferSize); /* Clone waves and plugins first in their own lists. */ if (oldChannel.samplePlayer && oldChannel.samplePlayer->hasWave()) { const Wave& oldWave = *oldChannel.samplePlayer->getWave(); m_model.addShared(waveManager.createFromWave(oldWave, 0, oldWave.getBuffer().countFrames())); loadChannel(newChannel, &m_model.backShared()); } #ifdef WITH_VST /* Overwrite existing plug-ins in new channel with a new vector of plug-ins, as currently new channel has cloned plug-ins from the old one (with same ID). */ std::vector newPlugins; for (const Plugin* plugin : oldChannel.plugins) { m_model.addShared(pluginManager.makePlugin(*plugin, sampleRate, bufferSize, sequencer)); newPlugins.push_back(&m_model.backShared()); } newChannel.plugins = newPlugins; #endif /* Then push the new channel in the channels vector. */ m_model.get().channels.push_back(newChannel); m_model.swap(model::SwapType::HARD); } /* -------------------------------------------------------------------------- */ void MixerHandler::freeChannel(ID channelId) { assert(onChannelsAltered != nullptr); Channel& ch = m_model.get().getChannel(channelId); assert(ch.samplePlayer); const Wave* wave = ch.samplePlayer->getWave(); loadChannel(ch, nullptr); m_model.swap(model::SwapType::HARD); if (wave != nullptr) m_model.removeShared(*wave); onChannelsAltered(); } /* -------------------------------------------------------------------------- */ void MixerHandler::freeAllChannels() { assert(onChannelsAltered != nullptr); for (Channel& ch : m_model.get().channels) if (ch.samplePlayer) loadChannel(ch, nullptr); m_model.swap(model::SwapType::HARD); m_model.clearShared(); onChannelsAltered(); } /* -------------------------------------------------------------------------- */ void MixerHandler::deleteChannel(ID channelId) { assert(onChannelsAltered != nullptr); const Channel& ch = m_model.get().getChannel(channelId); const Wave* wave = ch.samplePlayer ? ch.samplePlayer->getWave() : nullptr; #ifdef WITH_VST const std::vector plugins = ch.plugins; #endif u::vector::removeIf(m_model.get().channels, [channelId](const Channel& c) { return c.id == channelId; }); m_model.swap(model::SwapType::HARD); if (wave != nullptr) m_model.removeShared(*wave); updateSoloCount(); onChannelsAltered(); } /* -------------------------------------------------------------------------- */ void MixerHandler::renameChannel(ID channelId, const std::string& name) { m_model.get().getChannel(channelId).name = name; m_model.swap(model::SwapType::HARD); } /* -------------------------------------------------------------------------- */ void MixerHandler::updateSoloCount() { bool hasSolos = forAnyChannel([](const Channel& ch) { return !ch.isInternal() && ch.isSoloed(); }); m_model.get().mixer.hasSolos = hasSolos; m_model.swap(model::SwapType::NONE); } /* -------------------------------------------------------------------------- */ void MixerHandler::setInToOut(bool v) { m_model.get().mixer.inToOut = v; m_model.swap(model::SwapType::NONE); } /* -------------------------------------------------------------------------- */ float MixerHandler::getInVol() const { return m_model.get().getChannel(Mixer::MASTER_IN_CHANNEL_ID).volume; } float MixerHandler::getOutVol() const { return m_model.get().getChannel(Mixer::MASTER_OUT_CHANNEL_ID).volume; } bool MixerHandler::getInToOut() const { return m_model.get().mixer.inToOut; } /* -------------------------------------------------------------------------- */ void MixerHandler::startInputRec(Frame currentFrame) { m_mixer.startInputRec(currentFrame); } /* -------------------------------------------------------------------------- */ Frame MixerHandler::stopInputRec() { return m_mixer.stopInputRec(); } /* -------------------------------------------------------------------------- */ void MixerHandler::finalizeInputRec(Frame recordedFrames, Frame currentFrame) { for (Channel* ch : getRecordableChannels()) recordChannel(*ch, recordedFrames, currentFrame); for (Channel* ch : getOverdubbableChannels()) overdubChannel(*ch, currentFrame); m_mixer.clearRecBuffer(); onChannelsAltered(); } /* -------------------------------------------------------------------------- */ bool MixerHandler::hasInputRecordableChannels() const { return forAnyChannel([](const Channel& ch) { return ch.canInputRec(); }); } bool MixerHandler::hasActionRecordableChannels() const { return forAnyChannel([](const Channel& ch) { return ch.canActionRec(); }); } bool MixerHandler::hasLogicalSamples() const { return forAnyChannel([](const Channel& ch) { return ch.samplePlayer && ch.samplePlayer->hasLogicalWave(); }); } bool MixerHandler::hasEditedSamples() const { return forAnyChannel([](const Channel& ch) { return ch.samplePlayer && ch.samplePlayer->hasEditedWave(); }); } bool MixerHandler::hasActions() const { return forAnyChannel([](const Channel& ch) { return ch.hasActions; }); } bool MixerHandler::hasAudioData() const { return forAnyChannel([](const Channel& ch) { return ch.samplePlayer && ch.samplePlayer->hasWave(); }); } /* -------------------------------------------------------------------------- */ bool MixerHandler::forAnyChannel(std::function f) const { return std::any_of(m_model.get().channels.begin(), m_model.get().channels.end(), f); } /* -------------------------------------------------------------------------- */ void MixerHandler::loadChannel(Channel& ch, Wave* w) const { ch.samplePlayer->loadWave(*ch.shared, w); ch.name = w != nullptr ? w->getBasename(/*ext=*/false) : ""; } /* -------------------------------------------------------------------------- */ std::vector MixerHandler::getChannelsIf(std::function f) { std::vector out; for (Channel& ch : m_model.get().channels) if (f(ch)) out.push_back(&ch); return out; } std::vector MixerHandler::getRecordableChannels() { return getChannelsIf([](const Channel& c) { return c.canInputRec() && !c.hasWave(); }); } std::vector MixerHandler::getOverdubbableChannels() { return getChannelsIf([](const Channel& c) { return c.canInputRec() && c.hasWave(); }); } /* -------------------------------------------------------------------------- */ void MixerHandler::setupChannelPostRecording(Channel& ch, Frame currentFrame) { /* Start sample channels in loop mode right away. */ if (ch.samplePlayer->isAnyLoopMode()) ch.samplePlayer->kickIn(*ch.shared, currentFrame); /* Disable 'arm' button if overdub protection is on. */ if (ch.audioReceiver->overdubProtection == true) ch.armed = false; } /* -------------------------------------------------------------------------- */ void MixerHandler::recordChannel(Channel& ch, Frame recordedFrames, Frame currentFrame) { assert(onChannelRecorded != nullptr); std::unique_ptr wave = onChannelRecorded(recordedFrames); G_DEBUG("Created new Wave, size=" << wave->getBuffer().countFrames()); /* Copy up to wave.getSize() from the mixer's input buffer into wave's. */ wave->getBuffer().set(m_mixer.getRecBuffer(), wave->getBuffer().countFrames()); /* Update channel with the new Wave. */ m_model.addShared(std::move(wave)); loadChannel(ch, &m_model.backShared()); setupChannelPostRecording(ch, currentFrame); m_model.swap(model::SwapType::HARD); } /* -------------------------------------------------------------------------- */ void MixerHandler::overdubChannel(Channel& ch, Frame currentFrame) { Wave* wave = ch.samplePlayer->getWave(); /* Need model::DataLock here, as data might be being read by the audio thread at the same time. */ model::DataLock lock = m_model.lockData(); wave->getBuffer().sum(m_mixer.getRecBuffer(), /*gain=*/1.0f); wave->setLogical(true); setupChannelPostRecording(ch, currentFrame); } } // namespace giada::m giada-0.22.0/src/core/mixerHandler.h000066400000000000000000000123601425106661500171620ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_MIXER_HANDLER_H #define G_MIXER_HANDLER_H #include "core/plugins/pluginManager.h" #include "core/waveManager.h" #include "types.h" #include #include #include namespace giada::m::model { class Model; } namespace giada::m { class Channel; class Wave; class Mixer; class Plugin; class ChannelManager; class Sequencer; class MixerHandler final { public: MixerHandler(model::Model&, Mixer&); /* hasLogicalSamples True if 1 or more samples are logical (memory only, such as takes). */ bool hasLogicalSamples() const; /* hasEditedSamples True if 1 or more samples have been edited via Sample Editor. */ bool hasEditedSamples() const; /* has(Input|Action)RecordableChannels Tells whether Mixer has one or more input or action recordable channels. */ bool hasInputRecordableChannels() const; bool hasActionRecordableChannels() const; /* hasActions True if at least one Channel has actions recorded in it. */ bool hasActions() const; /* hasAudioData True if at least one Sample Channel has some audio recorded in it. */ bool hasAudioData() const; float getInVol() const; float getOutVol() const; bool getInToOut() const; /* reset Brings everything back to the initial state. */ void reset(Frame framesInLoop, Frame framesInBuffer, ChannelManager&); /* addChannel Adds a new channel of type 'type' into the channels stack. Returns the new channel ID. */ Channel& addChannel(ChannelType type, ID columnId, int bufferSize, ChannelManager&); /* loadChannel Loads a new Wave inside a Sample Channel. */ void loadChannel(ID channelId, std::unique_ptr w); /* addAndLoadChannel Creates a new channels, fills it with a Wave and then add it to the stack. */ void addAndLoadChannel(ID columnId, std::unique_ptr w, int bufferSize, ChannelManager&); /* freeChannel Unloads existing Wave from a Sample Channel. */ void freeChannel(ID channelId); /* deleteChannel Completely removes a channel from the stack. */ void deleteChannel(ID channelId); #ifdef WITH_VST void cloneChannel(ID channelId, int sampleRate, int bufferSize, ChannelManager&, WaveManager&, const Sequencer&, PluginManager&); #else void cloneChannel(ID channelId, int bufferSize, ChannelManager&, WaveManager&); #endif void renameChannel(ID channelId, const std::string& name); void freeAllChannels(); void setInToOut(bool v); /* updateSoloCount Updates the number of solo-ed channels in mixer. */ void updateSoloCount(); /* startInputRec Initializes Mixer for input recording. */ void startInputRec(Frame currentFrame); /* stopInputRec Terminates input recording in Mixer. Returns the number of recorded frames. Call finalizeInputRec() if you really want to finish the input recording operation. */ Frame stopInputRec(); /* finalizeInputRec Fills armed Sample Channels with audio data coming from an input recording session. */ void finalizeInputRec(Frame recordedFrames, Frame currentFrame); /* onChannelsAltered Fired when something is done on channels (added, removed, loaded, ...). */ std::function onChannelsAltered; /* onChannelRecorded Fired during the input recording finalization, when a new empty Wave must be added to each armed channel in order to store recorded audio coming from Mixer. */ std::function(Frame)> onChannelRecorded; private: bool forAnyChannel(std::function f) const; void loadChannel(Channel&, Wave*) const; std::vector getChannelsIf(std::function f); std::vector getRecordableChannels(); std::vector getOverdubbableChannels(); void setupChannelPostRecording(Channel& ch, Frame currentFrame); /* recordChannel Records the current Mixer audio input data into an empty channel. */ void recordChannel(Channel& ch, Frame recordedFrames, Frame currentFrame); /* overdubChannel Records the current Mixer audio input data into a channel with an existing Wave, overdub mode. */ void overdubChannel(Channel& ch, Frame currentFrame); model::Model& m_model; Mixer& m_mixer; }; } // namespace giada::m #endif giada-0.22.0/src/core/model/000077500000000000000000000000001425106661500154655ustar00rootroot00000000000000giada-0.22.0/src/core/model/mixer.cpp000066400000000000000000000056071425106661500173250ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/model/mixer.h" namespace giada::m::model { Mixer::Shared& Mixer::Shared::operator=(const Mixer::Shared& o) { if (this == &o) return *this; active.store(o.active.load()); peakOutL.store(0.0f); peakOutR.store(0.0f); peakInL.store(0.0f); peakInR.store(0.0f); inputTracker.store(0); return *this; } /* -------------------------------------------------------------------------- */ bool Mixer::a_isActive() const { return shared->active.load() == true; } /* -------------------------------------------------------------------------- */ Frame Mixer::a_getInputTracker() const { return shared->inputTracker.load(); } /* -------------------------------------------------------------------------- */ void Mixer::a_setActive(bool isActive) const { shared->active.store(isActive); } /* -------------------------------------------------------------------------- */ void Mixer::a_setInputTracker(Frame f) const { shared->inputTracker.store(f); } /* -------------------------------------------------------------------------- */ Peak Mixer::a_getPeakOut() const { return {shared->peakOutL.load(), shared->peakOutR.load()}; } Peak Mixer::a_getPeakIn() const { return {shared->peakInL.load(), shared->peakInR.load()}; } /* -------------------------------------------------------------------------- */ void Mixer::a_setPeakOut(Peak p) const { shared->peakOutL.store(p.left); shared->peakOutR.store(p.right); } void Mixer::a_setPeakIn(Peak p) const { shared->peakInL.store(p.left); shared->peakInR.store(p.right); } /* -------------------------------------------------------------------------- */ mcl::AudioBuffer& Mixer::getRecBuffer() const { return shared->recBuffer; } mcl::AudioBuffer& Mixer::getInBuffer() const { return shared->inBuffer; } } // namespace giada::m::model giada-0.22.0/src/core/model/mixer.h000066400000000000000000000047241425106661500167710ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_MODEL_MIXER_H #define G_MODEL_MIXER_H #include "core/const.h" #include "core/types.h" #include "core/weakAtomic.h" #include "deps/mcl-audio-buffer/src/audioBuffer.hpp" #include namespace giada::m::model { class Mixer { friend class Model; public: bool a_isActive() const; Frame a_getInputTracker() const; Peak a_getPeakOut() const; Peak a_getPeakIn() const; void a_setActive(bool) const; void a_setInputTracker(Frame) const; void a_setPeakOut(Peak) const; void a_setPeakIn(Peak) const; mcl::AudioBuffer& getRecBuffer() const; mcl::AudioBuffer& getInBuffer() const; bool hasSolos = false; bool inToOut = false; bool limitOutput = false; bool allowsOverdub = false; Frame maxFramesToRec = 0; float recTriggerLevel = 0.0f; private: struct Shared { Shared& operator=(const Shared&); std::atomic active = false; WeakAtomic peakOutL = 0.0f; WeakAtomic peakOutR = 0.0f; WeakAtomic peakInL = 0.0f; WeakAtomic peakInR = 0.0f; WeakAtomic inputTracker = 0; /* recBuffer Working buffer for audio recording. */ mcl::AudioBuffer recBuffer; /* inBuffer Working buffer for input channel. Used for the in->out bridge. */ mcl::AudioBuffer inBuffer; }; Shared* shared = nullptr; }; } // namespace giada::m::model #endif giada-0.22.0/src/core/model/model.cpp000066400000000000000000000216251425106661500172770ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/model/model.h" #include #include #ifdef G_DEBUG_MODE #include "core/channels/channelManager.h" #endif using namespace mcl; namespace giada::m::model { namespace { template auto getIter_(const std::vector>& source, ID id) { return u::vector::findIf(source, [id](const std::unique_ptr& p) { return p->id == id; }); } /* -------------------------------------------------------------------------- */ template auto* get_(S& source, ID id) { auto it = getIter_(source, id); return it == source.end() ? nullptr : it->get(); } /* -------------------------------------------------------------------------- */ template void remove_(D& dest, T& ref) { u::vector::removeIf(dest, [&ref](const auto& other) { return other.get() == &ref; }); } } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ DataLock::DataLock(Model& m, SwapType t) : m_model(m) , m_swapType(t) { m_model.get().locked = true; m_model.swap(SwapType::NONE); } DataLock::~DataLock() { m_model.get().locked = false; m_model.swap(m_swapType); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ Channel& Layout::getChannel(ID id) { return const_cast(const_cast(this)->getChannel(id)); } const Channel& Layout::getChannel(ID id) const { auto it = std::find_if(channels.begin(), channels.end(), [id](const Channel& c) { return c.id == id; }); assert(it != channels.end()); return *it; } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ Model::Model() : onSwap(nullptr) { reset(); } /* -------------------------------------------------------------------------- */ void Model::reset() { get() = {}; m_shared = {}; get().sequencer.shared = &m_shared.sequencerShared; get().mixer.shared = &m_shared.mixerShared; get().recorder.shared = &m_shared.recorderShared; swap(SwapType::NONE); } /* -------------------------------------------------------------------------- */ Layout& Model::get() { return m_layout.get(); } const Layout& Model::get() const { return m_layout.get(); } LayoutLock Model::get_RT() { return LayoutLock(m_layout); } void Model::swap(SwapType t) { m_layout.swap(); if (onSwap) onSwap(t); } /* -------------------------------------------------------------------------- */ DataLock Model::lockData(SwapType t) { return DataLock(*this, t); } /* -------------------------------------------------------------------------- */ bool Model::isLocked() const { return m_layout.isLocked(); } /* -------------------------------------------------------------------------- */ template T& Model::getAllShared() { #ifdef WITH_VST if constexpr (std::is_same_v) return m_shared.plugins; #endif if constexpr (std::is_same_v) return m_shared.waves; if constexpr (std::is_same_v) return m_shared.actions; if constexpr (std::is_same_v) return m_shared.channelsShared; assert(false); } #ifdef WITH_VST template PluginPtrs& Model::getAllShared(); #endif template WavePtrs& Model::getAllShared(); template Actions::Map& Model::getAllShared(); template ChannelSharedPtrs& Model::getAllShared(); /* -------------------------------------------------------------------------- */ template T* Model::findShared(ID id) { #ifdef WITH_VST if constexpr (std::is_same_v) return get_(m_shared.plugins, id); #endif if constexpr (std::is_same_v) return get_(m_shared.waves, id); assert(false); } #ifdef WITH_VST template Plugin* Model::findShared(ID id); #endif template Wave* Model::findShared(ID id); /* -------------------------------------------------------------------------- */ template void Model::addShared(T obj) { #ifdef WITH_VST if constexpr (std::is_same_v) m_shared.plugins.push_back(std::move(obj)); #endif if constexpr (std::is_same_v) m_shared.waves.push_back(std::move(obj)); if constexpr (std::is_same_v) m_shared.channelsShared.push_back(std::move(obj)); } #ifdef WITH_VST template void Model::addShared(PluginPtr p); #endif template void Model::addShared(WavePtr p); template void Model::addShared(ChannelSharedPtr p); /* -------------------------------------------------------------------------- */ template void Model::removeShared(const T& ref) { #ifdef WITH_VST if constexpr (std::is_same_v) remove_(m_shared.plugins, ref); #endif if constexpr (std::is_same_v) remove_(m_shared.waves, ref); } #ifdef WITH_VST template void Model::removeShared(const Plugin& t); #endif template void Model::removeShared(const Wave& t); /* -------------------------------------------------------------------------- */ template T& Model::backShared() { #ifdef WITH_VST if constexpr (std::is_same_v) return *m_shared.plugins.back().get(); #endif if constexpr (std::is_same_v) return *m_shared.waves.back().get(); if constexpr (std::is_same_v) return *m_shared.channelsShared.back().get(); } #ifdef WITH_VST template Plugin& Model::backShared(); #endif template Wave& Model::backShared(); template ChannelShared& Model::backShared(); /* -------------------------------------------------------------------------- */ template void Model::clearShared() { #ifdef WITH_VST if constexpr (std::is_same_v) m_shared.plugins.clear(); #endif if constexpr (std::is_same_v) m_shared.waves.clear(); } #ifdef WITH_VST template void Model::clearShared(); #endif template void Model::clearShared(); /* -------------------------------------------------------------------------- */ #ifdef G_DEBUG_MODE void Model::debug() { puts("======== SYSTEM STATUS ========"); puts("model::layout.channels"); int i = 0; for (const Channel& c : get().channels) { printf("\t%d) - ID=%d name='%s' type=%d columnID=%d shared=%p\n", i++, c.id, c.name.c_str(), (int)c.type, c.columnId, (void*)&c.shared); #ifdef WITH_VST if (c.plugins.size() > 0) { puts("\t\tplugins:"); for (const auto& p : c.plugins) printf("\t\t\t%p - ID=%d\n", (void*)p, p->id); } #endif } puts("model::state.channels"); i = 0; for (const auto& c : m_shared.channelsShared) { printf("\t%d) - %p\n", i++, (void*)c.get()); } puts("model::data.waves"); i = 0; for (const auto& w : m_shared.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] : getAllShared()) { 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); } #ifdef WITH_VST puts("model::data.plugins"); i = 0; for (const auto& p : m_shared.plugins) printf("\t%d) %p - ID=%d\n", i++, (void*)p.get(), p->id); #endif } #endif // G_DEBUG_MODE } // namespace giada::m::model giada-0.22.0/src/core/model/model.h000066400000000000000000000127371425106661500167500ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_MODEL_H #define G_MODEL_H #include "core/channels/channel.h" #include "core/const.h" #include "core/model/mixer.h" #include "core/model/recorder.h" #include "core/model/sequencer.h" #include "core/plugins/plugin.h" #include "core/recorder.h" #include "core/wave.h" #include "deps/mcl-atomic-swapper/src/atomic-swapper.hpp" #include "deps/mcl-audio-buffer/src/audioBuffer.hpp" #include "src/core/actions/actions.h" #include "utils/vector.h" #include namespace giada::m::model { struct MidiIn { bool enabled = false; int filter = -1; uint32_t rewind = 0x0; uint32_t startStop = 0x0; uint32_t actionRec = 0x0; uint32_t inputRec = 0x0; uint32_t volumeIn = 0x0; uint32_t volumeOut = 0x0; uint32_t beatDouble = 0x0; uint32_t beatHalf = 0x0; uint32_t metronome = 0x0; }; struct Layout { Channel& getChannel(ID id); const Channel& getChannel(ID id) const; Sequencer sequencer; Mixer mixer; Recorder recorder; MidiIn midiIn; std::vector channels; /* 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; }; /* LayoutLock Alias for a REALTIME scoped lock provided by the Swapper class. Use this in the real-time thread to lock the Layout. */ using LayoutLock = mcl::AtomicSwapper::RtLock; /* 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 occurred in the layout. */ enum class SwapType { HARD, SOFT, NONE }; #ifdef WITH_VST using PluginPtr = std::unique_ptr; #endif using WavePtr = std::unique_ptr; using ChannelSharedPtr = std::unique_ptr; #ifdef WITH_VST using PluginPtrs = std::vector; #endif using WavePtrs = std::vector; using ChannelSharedPtrs = std::vector; /* -------------------------------------------------------------------------- */ class DataLock; class Model { public: Model(); bool isLocked() const; /* lockData Returns a scoped locker DataLock object. Use this when you want to lock the model: a locked model won't be processed by Mixer. */ [[nodiscard]] DataLock lockData(SwapType t = SwapType::HARD); /* reser Resets the internal layout to default. */ void reset(); /* get_RT Returns a LayoutLock object for REALTIME processing. Access layout by calling LayoutLock::get() method (returns ready-only Layout). */ LayoutLock get_RT(); /* get Returns a reference to the NON-REALTIME layout structure. */ Layout& get(); const Layout& get() const; /* swap Swap non-rt layout with the rt one. See 'SwapType' notes above. */ void swap(SwapType t); template T& getAllShared(); /* findShared Finds something in the shared data given an ID. Returns nullptr if the object is not found. */ template T* findShared(ID id); /* addShared Adds some shared data (by moving it). */ template void addShared(T); template void removeShared(const T&); /* backShared Returns a reference to the last added shared item. */ template T& backShared(); template void clearShared(); #ifdef G_DEBUG_MODE void debug(); #endif /* onSwap Optional callback fired when the layout has been swapped. Useful for listening to model changes. */ std::function onSwap = nullptr; private: struct Shared { Sequencer::Shared sequencerShared; Mixer::Shared mixerShared; Recorder::Shared recorderShared; std::vector> channelsShared; std::vector> waves; Actions::Map actions; #ifdef WITH_VST std::vector> plugins; #endif }; mcl::AtomicSwapper m_layout; Shared m_shared; }; /* -------------------------------------------------------------------------- */ class DataLock { public: DataLock(Model&, SwapType t); ~DataLock(); private: Model& m_model; SwapType m_swapType; }; } // namespace giada::m::model #endif giada-0.22.0/src/core/model/recorder.cpp000066400000000000000000000030711425106661500177770ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/model/recorder.h" namespace giada::m::model { bool Recorder::a_isRecordingAction() const { return shared->isRecordingAction.load(); } bool Recorder::a_isRecordingInput() const { return shared->isRecordingInput.load(); } void Recorder::a_setRecordingAction(bool b) const { shared->isRecordingAction.store(b); } void Recorder::a_setRecordingInput(bool b) const { shared->isRecordingInput.store(b); } } // namespace giada::m::model giada-0.22.0/src/core/model/recorder.h000066400000000000000000000031531425106661500174450ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_MODEL_RECORDER_H #define G_MODEL_RECORDER_H #include "core/weakAtomic.h" namespace giada::m::model { class Recorder { friend class Model; public: bool a_isRecordingAction() const; bool a_isRecordingInput() const; void a_setRecordingAction(bool) const; void a_setRecordingInput(bool) const; private: struct Shared { WeakAtomic isRecordingAction = false; WeakAtomic isRecordingInput = false; }; Shared* shared = nullptr; }; } // namespace giada::m::model #endif giada-0.22.0/src/core/model/sequencer.cpp000066400000000000000000000053741425106661500201740ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/model/sequencer.h" namespace giada::m::model { bool Sequencer::isActive() const { return status == SeqStatus::RUNNING || status == SeqStatus::WAITING; } /* -------------------------------------------------------------------------- */ bool Sequencer::canQuantize() const { return quantize > 0 && status == SeqStatus::RUNNING; } /* -------------------------------------------------------------------------- */ bool Sequencer::isRunning() const { return status == SeqStatus::RUNNING; } /* -------------------------------------------------------------------------- */ bool Sequencer::a_isOnBar() const { const int currentFrame = shared->currentFrame.load(); if (status == SeqStatus::WAITING || currentFrame == 0) return false; return currentFrame % framesInBar == 0; } /* -------------------------------------------------------------------------- */ bool Sequencer::a_isOnBeat() const { return shared->currentFrame.load() % framesInBeat == 0; } /* -------------------------------------------------------------------------- */ bool Sequencer::a_isOnFirstBeat() const { return shared->currentFrame.load() == 0; } /* -------------------------------------------------------------------------- */ Frame Sequencer::a_getCurrentFrame() const { return shared->currentFrame.load(); } Frame Sequencer::a_getCurrentBeat() const { return shared->currentBeat.load(); } /* -------------------------------------------------------------------------- */ void Sequencer::a_setCurrentFrame(Frame f) const { shared->currentFrame.store(f); } void Sequencer::a_setCurrentBeat(Frame f) const { shared->currentBeat.store(f); } } // namespace giada::m::model giada-0.22.0/src/core/model/sequencer.h000066400000000000000000000047431425106661500176400ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_MODEL_SEQUENCER_H #define G_MODEL_SEQUENCER_H #include "core/const.h" #include "core/types.h" #include "core/weakAtomic.h" #include "deps/mcl-audio-buffer/src/audioBuffer.hpp" namespace giada::m::model { class Sequencer { friend class Model; public: /* isRunning When sequencer is actually moving forward, i.e. SeqStatus == RUNNING. */ bool isRunning() const; /* isActive Sequencer is enabled, but might be in wait mode, i.e. SeqStatus == RUNNING or SeqStatus == WAITING. */ bool isActive() const; /* canQuantize Sequencer can quantize only if it's running and quantizer is enabled. */ bool canQuantize() const; bool a_isOnBar() const; bool a_isOnBeat() const; bool a_isOnFirstBeat() const; Frame a_getCurrentFrame() const; Frame a_getCurrentBeat() const; void a_setCurrentFrame(Frame) const; void a_setCurrentBeat(Frame) const; SeqStatus status = SeqStatus::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; private: struct Shared { WeakAtomic currentFrame = 0; WeakAtomic currentBeat = 0; }; Shared* shared = nullptr; }; } // namespace giada::m::model #endif giada-0.22.0/src/core/model/storage.cpp000066400000000000000000000151531425106661500176420ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2019 Giovanni A. Zuliani | Monocasual * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/model/storage.h" #include "core/channels/channelManager.h" #include "core/conf.h" #include "core/engine.h" #include "core/kernelAudio.h" #include "core/model/model.h" #include "core/patch.h" #include "core/plugins/pluginManager.h" #include "core/sequencer.h" #include "core/waveManager.h" #include "src/core/actions/actionRecorder.h" #include #include extern giada::m::Engine g_engine; namespace giada::m::model { namespace { void loadChannels_(const std::vector& channels, int samplerate) { float samplerateRatio = g_engine.kernelAudio.getSampleRate() / static_cast(samplerate); for (const Patch::Channel& pchannel : channels) g_engine.model.get().channels.push_back( g_engine.channelManager.deserializeChannel(pchannel, samplerateRatio, g_engine.kernelAudio.getBufferSize())); } /* -------------------------------------------------------------------------- */ void loadActions_(const std::vector& pactions) { g_engine.model.getAllShared() = std::move(g_engine.actionRecorder.deserializeActions(pactions)); } } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ void store(Patch::Data& patch) { const Layout& layout = g_engine.model.get(); patch.bars = layout.sequencer.bars; patch.beats = layout.sequencer.beats; patch.bpm = layout.sequencer.bpm; patch.quantize = layout.sequencer.quantize; patch.metronome = g_engine.sequencer.isMetronomeOn(); // TODO - addShared bool metronome to Layout patch.samplerate = g_engine.kernelAudio.getSampleRate(); #ifdef WITH_VST patch.plugins.clear(); for (const auto& p : g_engine.model.getAllShared()) patch.plugins.push_back(g_engine.pluginManager.serializePlugin(*p)); #endif patch.actions = g_engine.actionRecorder.serializeActions(g_engine.model.getAllShared()); patch.waves.clear(); for (const auto& w : g_engine.model.getAllShared()) patch.waves.push_back(g_engine.waveManager.serializeWave(*w)); patch.channels.clear(); for (const Channel& c : layout.channels) patch.channels.push_back(g_engine.channelManager.serializeChannel(c)); } /* -------------------------------------------------------------------------- */ void store(Conf::Data& conf) { const Layout& layout = g_engine.model.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; } /* -------------------------------------------------------------------------- */ LoadState load(const Patch::Data& patch) { DataLock lock = g_engine.model.lockData(SwapType::NONE); /* Clear and re-initialize channels first. */ g_engine.model.get().channels = {}; g_engine.model.getAllShared().clear(); LoadState state; /* Load external data first: plug-ins and waves. */ #ifdef WITH_VST g_engine.model.getAllShared().clear(); for (const Patch::Plugin& pplugin : patch.plugins) { std::unique_ptr p = g_engine.pluginManager.deserializePlugin( pplugin, g_engine.kernelAudio.getSampleRate(), g_engine.kernelAudio.getBufferSize(), g_engine.sequencer); if (!p->valid) state.missingPlugins.push_back(pplugin.path); g_engine.model.getAllShared().push_back(std::move(p)); } #endif g_engine.model.getAllShared().clear(); for (const Patch::Wave& pwave : patch.waves) { std::unique_ptr w = g_engine.waveManager.deserializeWave(pwave, g_engine.kernelAudio.getSampleRate(), g_engine.conf.data.rsmpQuality); if (w != nullptr) g_engine.model.getAllShared().push_back(std::move(w)); else state.missingWaves.push_back(pwave.path); } /* Then load up channels, actions and global properties. */ loadChannels_(patch.channels, g_engine.patch.data.samplerate); loadActions_(patch.actions); g_engine.model.get().sequencer.status = SeqStatus::STOPPED; g_engine.model.get().sequencer.bars = patch.bars; g_engine.model.get().sequencer.beats = patch.beats; g_engine.model.get().sequencer.bpm = patch.bpm; g_engine.model.get().sequencer.quantize = patch.quantize; return state; } /* -------------------------------------------------------------------------- */ void load(const Conf::Data& c) { g_engine.model.get().midiIn.enabled = c.midiInEnabled; g_engine.model.get().midiIn.filter = c.midiInFilter; g_engine.model.get().midiIn.rewind = c.midiInRewind; g_engine.model.get().midiIn.startStop = c.midiInStartStop; g_engine.model.get().midiIn.actionRec = c.midiInActionRec; g_engine.model.get().midiIn.inputRec = c.midiInInputRec; g_engine.model.get().midiIn.volumeIn = c.midiInVolumeIn; g_engine.model.get().midiIn.volumeOut = c.midiInVolumeOut; g_engine.model.get().midiIn.beatDouble = c.midiInBeatDouble; g_engine.model.get().midiIn.beatHalf = c.midiInBeatHalf; g_engine.model.get().midiIn.metronome = c.midiInMetronome; g_engine.model.swap(SwapType::NONE); } } // namespace giada::m::model giada-0.22.0/src/core/model/storage.h000066400000000000000000000026651425106661500173130ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2019 Giovanni A. Zuliani | Monocasual * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_MODEL_STORAGE_H #define G_MODEL_STORAGE_H #include "core/conf.h" #include "core/engine.h" #include "core/patch.h" namespace giada::m::model { void store(Conf::Data& c); void store(Patch::Data& p); LoadState load(const Patch::Data& p); void load(const Conf::Data& c); } // namespace giada::m::model #endif giada-0.22.0/src/core/patch.cpp000066400000000000000000000402571425106661500162000ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "patch.h" #include "core/mixer.h" #include "utils/log.h" #include "utils/math.h" #include #include namespace nl = nlohmann; namespace giada::m { namespace { void readCommons_(Patch::Data& patch, const nl::json& j) { patch.name = j.value(PATCH_KEY_NAME, G_DEFAULT_PATCH_NAME); patch.bars = j.value(PATCH_KEY_BARS, G_DEFAULT_BARS); patch.beats = j.value(PATCH_KEY_BEATS, G_DEFAULT_BEATS); patch.bpm = j.value(PATCH_KEY_BPM, G_DEFAULT_BPM); patch.quantize = j.value(PATCH_KEY_QUANTIZE, G_DEFAULT_QUANTIZE); patch.lastTakeId = j.value(PATCH_KEY_LAST_TAKE_ID, 0); patch.samplerate = j.value(PATCH_KEY_SAMPLERATE, G_DEFAULT_SAMPLERATE); patch.metronome = j.value(PATCH_KEY_METRONOME, false); } /* -------------------------------------------------------------------------- */ void readColumns_(Patch::Data& patch, const nl::json& j) { ID id = 0; for (const auto& jcol : j[PATCH_KEY_COLUMNS]) { Patch::Column c; c.id = jcol.value(PATCH_KEY_COLUMN_ID, ++id); c.width = jcol.value(PATCH_KEY_COLUMN_WIDTH, G_DEFAULT_COLUMN_WIDTH); patch.columns.push_back(c); } } /* -------------------------------------------------------------------------- */ #ifdef WITH_VST void readPlugins_(Patch::Data& patch, const nl::json& j) { if (!j.contains(PATCH_KEY_PLUGINS)) return; ID id = 0; for (const auto& jplugin : j[PATCH_KEY_PLUGINS]) { Patch::Plugin p; p.id = jplugin.value(PATCH_KEY_PLUGIN_ID, ++id); p.path = jplugin.value(PATCH_KEY_PLUGIN_PATH, ""); p.bypass = jplugin.value(PATCH_KEY_PLUGIN_BYPASS, false); if (patch.version < Patch::Version{0, 17, 0}) for (const auto& jparam : jplugin[PATCH_KEY_PLUGIN_PARAMS]) p.params.push_back(jparam); else p.state = jplugin.value(PATCH_KEY_PLUGIN_STATE, ""); for (const auto& jmidiParam : jplugin[PATCH_KEY_PLUGIN_MIDI_IN_PARAMS]) p.midiInParams.push_back(jmidiParam); patch.plugins.push_back(p); } } #endif /* -------------------------------------------------------------------------- */ void readWaves_(Patch::Data& patch, const nl::json& j, const std::string& basePath) { if (!j.contains(PATCH_KEY_WAVES)) return; ID id = 0; for (const auto& jwave : j[PATCH_KEY_WAVES]) { Patch::Wave w; w.id = jwave.value(PATCH_KEY_WAVE_ID, ++id); w.path = u::fs::join(basePath, jwave.value(PATCH_KEY_WAVE_PATH, "")); patch.waves.push_back(w); } } /* -------------------------------------------------------------------------- */ void readActions_(Patch::Data& patch, const nl::json& j) { if (!j.contains(PATCH_KEY_ACTIONS)) return; ID id = 0; for (const auto& jaction : j[PATCH_KEY_ACTIONS]) { Patch::Action a; a.id = jaction.value(G_PATCH_KEY_ACTION_ID, ++id); a.channelId = jaction.value(G_PATCH_KEY_ACTION_CHANNEL, 0); a.frame = jaction.value(G_PATCH_KEY_ACTION_FRAME, 0); a.event = jaction.value(G_PATCH_KEY_ACTION_EVENT, 0); a.prevId = jaction.value(G_PATCH_KEY_ACTION_PREV, 0); a.nextId = jaction.value(G_PATCH_KEY_ACTION_NEXT, 0); patch.actions.push_back(a); } } /* -------------------------------------------------------------------------- */ void readChannels_(Patch::Data& patch, const nl::json& j) { if (!j.contains(PATCH_KEY_CHANNELS)) return; ID defaultId = Mixer::PREVIEW_CHANNEL_ID; for (const auto& jchannel : j[PATCH_KEY_CHANNELS]) { Patch::Channel c; c.id = jchannel.value(PATCH_KEY_CHANNEL_ID, ++defaultId); c.type = static_cast(jchannel.value(PATCH_KEY_CHANNEL_TYPE, 1)); 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.key = jchannel.value(PATCH_KEY_CHANNEL_KEY, 0); c.mute = jchannel.value(PATCH_KEY_CHANNEL_MUTE, 0); c.solo = jchannel.value(PATCH_KEY_CHANNEL_SOLO, 0); c.pan = jchannel.value(PATCH_KEY_CHANNEL_PAN, 0.5f); c.hasActions = jchannel.value(PATCH_KEY_CHANNEL_HAS_ACTIONS, false); c.midiIn = jchannel.value(PATCH_KEY_CHANNEL_MIDI_IN, 0); c.midiInKeyPress = jchannel.value(PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS, 0); c.midiInKeyRel = jchannel.value(PATCH_KEY_CHANNEL_MIDI_IN_KEYREL, 0); c.midiInKill = jchannel.value(PATCH_KEY_CHANNEL_MIDI_IN_KILL, 0); c.midiInArm = jchannel.value(PATCH_KEY_CHANNEL_MIDI_IN_ARM, 0); c.midiInVolume = jchannel.value(PATCH_KEY_CHANNEL_MIDI_IN_VOLUME, 0); c.midiInMute = jchannel.value(PATCH_KEY_CHANNEL_MIDI_IN_MUTE, 0); c.midiInSolo = jchannel.value(PATCH_KEY_CHANNEL_MIDI_IN_SOLO, 0); c.midiInFilter = jchannel.value(PATCH_KEY_CHANNEL_MIDI_IN_FILTER, 0); c.midiOutL = jchannel.value(PATCH_KEY_CHANNEL_MIDI_OUT_L, 0); c.midiOutLplaying = jchannel.value(PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING, 0); c.midiOutLmute = jchannel.value(PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE, 0); c.midiOutLsolo = jchannel.value(PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO, 0); c.armed = jchannel.value(PATCH_KEY_CHANNEL_ARMED, false); c.mode = static_cast(jchannel.value(PATCH_KEY_CHANNEL_MODE, 1)); c.waveId = jchannel.value(PATCH_KEY_CHANNEL_WAVE_ID, 0); c.begin = jchannel.value(PATCH_KEY_CHANNEL_BEGIN, 0); c.end = jchannel.value(PATCH_KEY_CHANNEL_END, 0); c.shift = jchannel.value(PATCH_KEY_CHANNEL_SHIFT, 0); c.readActions = jchannel.value(PATCH_KEY_CHANNEL_READ_ACTIONS, false); c.pitch = jchannel.value(PATCH_KEY_CHANNEL_PITCH, G_DEFAULT_PITCH); c.inputMonitor = jchannel.value(PATCH_KEY_CHANNEL_INPUT_MONITOR, false); c.overdubProtection = jchannel.value(PATCH_KEY_CHANNEL_OVERDUB_PROTECTION, false); c.midiInVeloAsVol = jchannel.value(PATCH_KEY_CHANNEL_MIDI_IN_VELO_AS_VOL, 0); c.midiInReadActions = jchannel.value(PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS, 0); c.midiInPitch = jchannel.value(PATCH_KEY_CHANNEL_MIDI_IN_PITCH, 0); c.midiOut = jchannel.value(PATCH_KEY_CHANNEL_MIDI_OUT, 0); 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]) c.pluginIds.push_back(jplugin); #endif patch.channels.push_back(c); } } /* -------------------------------------------------------------------------- */ #ifdef WITH_VST void writePlugins_(const Patch::Data& patch, nl::json& j) { j[PATCH_KEY_PLUGINS] = nl::json::array(); for (const Patch::Plugin& p : patch.plugins) { nl::json jplugin; jplugin[PATCH_KEY_PLUGIN_ID] = p.id; jplugin[PATCH_KEY_PLUGIN_PATH] = p.path; jplugin[PATCH_KEY_PLUGIN_BYPASS] = p.bypass; jplugin[PATCH_KEY_PLUGIN_STATE] = p.state; 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_(const Patch::Data& patch, nl::json& j) { j[PATCH_KEY_COLUMNS] = nl::json::array(); for (const Patch::Column& column : patch.columns) { nl::json jcolumn; jcolumn[PATCH_KEY_COLUMN_ID] = column.id; jcolumn[PATCH_KEY_COLUMN_WIDTH] = column.width; j[PATCH_KEY_COLUMNS].push_back(jcolumn); } } /* -------------------------------------------------------------------------- */ void writeActions_(const Patch::Data& patch, nl::json& j) { j[PATCH_KEY_ACTIONS] = nl::json::array(); for (const Patch::Action& a : patch.actions) { nl::json jaction; jaction[G_PATCH_KEY_ACTION_ID] = a.id; jaction[G_PATCH_KEY_ACTION_CHANNEL] = a.channelId; jaction[G_PATCH_KEY_ACTION_FRAME] = a.frame; jaction[G_PATCH_KEY_ACTION_EVENT] = a.event; jaction[G_PATCH_KEY_ACTION_PREV] = a.prevId; jaction[G_PATCH_KEY_ACTION_NEXT] = a.nextId; j[PATCH_KEY_ACTIONS].push_back(jaction); } } /* -------------------------------------------------------------------------- */ void writeWaves_(const Patch::Data& patch, nl::json& j) { j[PATCH_KEY_WAVES] = nl::json::array(); for (const Patch::Wave& w : patch.waves) { nl::json jwave; jwave[PATCH_KEY_WAVE_ID] = w.id; jwave[PATCH_KEY_WAVE_PATH] = w.path; j[PATCH_KEY_WAVES].push_back(jwave); } } /* -------------------------------------------------------------------------- */ void writeCommons_(const Patch::Data& patch, nl::json& j) { j[PATCH_KEY_HEADER] = "GIADAPTC"; j[PATCH_KEY_VERSION_MAJOR] = G_VERSION_MAJOR; j[PATCH_KEY_VERSION_MINOR] = G_VERSION_MINOR; j[PATCH_KEY_VERSION_PATCH] = G_VERSION_PATCH; j[PATCH_KEY_NAME] = patch.name; j[PATCH_KEY_BARS] = patch.bars; j[PATCH_KEY_BEATS] = patch.beats; j[PATCH_KEY_BPM] = patch.bpm; j[PATCH_KEY_QUANTIZE] = patch.quantize; j[PATCH_KEY_LAST_TAKE_ID] = patch.lastTakeId; j[PATCH_KEY_SAMPLERATE] = patch.samplerate; j[PATCH_KEY_METRONOME] = patch.metronome; } /* -------------------------------------------------------------------------- */ void writeChannels_(const Patch::Data& patch, nl::json& j) { j[PATCH_KEY_CHANNELS] = nl::json::array(); for (const Patch::Channel& c : patch.channels) { nl::json jchannel; jchannel[PATCH_KEY_CHANNEL_ID] = c.id; jchannel[PATCH_KEY_CHANNEL_TYPE] = static_cast(c.type); jchannel[PATCH_KEY_CHANNEL_SIZE] = c.height; jchannel[PATCH_KEY_CHANNEL_NAME] = c.name; jchannel[PATCH_KEY_CHANNEL_COLUMN] = c.columnId; jchannel[PATCH_KEY_CHANNEL_MUTE] = c.mute; jchannel[PATCH_KEY_CHANNEL_SOLO] = c.solo; jchannel[PATCH_KEY_CHANNEL_VOLUME] = c.volume; jchannel[PATCH_KEY_CHANNEL_PAN] = c.pan; jchannel[PATCH_KEY_CHANNEL_HAS_ACTIONS] = c.hasActions; jchannel[PATCH_KEY_CHANNEL_ARMED] = c.armed; jchannel[PATCH_KEY_CHANNEL_MIDI_IN] = c.midiIn; jchannel[PATCH_KEY_CHANNEL_MIDI_IN_KEYREL] = c.midiInKeyRel; jchannel[PATCH_KEY_CHANNEL_MIDI_IN_KEYPRESS] = c.midiInKeyPress; jchannel[PATCH_KEY_CHANNEL_MIDI_IN_KILL] = c.midiInKill; jchannel[PATCH_KEY_CHANNEL_MIDI_IN_ARM] = c.midiInArm; jchannel[PATCH_KEY_CHANNEL_MIDI_IN_VOLUME] = c.midiInVolume; jchannel[PATCH_KEY_CHANNEL_MIDI_IN_MUTE] = c.midiInMute; jchannel[PATCH_KEY_CHANNEL_MIDI_IN_SOLO] = c.midiInSolo; jchannel[PATCH_KEY_CHANNEL_MIDI_IN_FILTER] = c.midiInFilter; jchannel[PATCH_KEY_CHANNEL_MIDI_OUT_L] = c.midiOutL; jchannel[PATCH_KEY_CHANNEL_MIDI_OUT_L_PLAYING] = c.midiOutLplaying; jchannel[PATCH_KEY_CHANNEL_MIDI_OUT_L_MUTE] = c.midiOutLmute; jchannel[PATCH_KEY_CHANNEL_MIDI_OUT_L_SOLO] = c.midiOutLsolo; jchannel[PATCH_KEY_CHANNEL_KEY] = c.key; jchannel[PATCH_KEY_CHANNEL_WAVE_ID] = c.waveId; jchannel[PATCH_KEY_CHANNEL_MODE] = static_cast(c.mode); jchannel[PATCH_KEY_CHANNEL_BEGIN] = c.begin; jchannel[PATCH_KEY_CHANNEL_END] = c.end; jchannel[PATCH_KEY_CHANNEL_SHIFT] = c.shift; jchannel[PATCH_KEY_CHANNEL_READ_ACTIONS] = c.readActions; jchannel[PATCH_KEY_CHANNEL_PITCH] = c.pitch; jchannel[PATCH_KEY_CHANNEL_INPUT_MONITOR] = c.inputMonitor; jchannel[PATCH_KEY_CHANNEL_OVERDUB_PROTECTION] = c.overdubProtection; jchannel[PATCH_KEY_CHANNEL_MIDI_IN_VELO_AS_VOL] = c.midiInVeloAsVol; jchannel[PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS] = c.midiInReadActions; jchannel[PATCH_KEY_CHANNEL_MIDI_IN_PITCH] = c.midiInPitch; jchannel[PATCH_KEY_CHANNEL_MIDI_OUT] = c.midiOut; jchannel[PATCH_KEY_CHANNEL_MIDI_OUT_CHAN] = c.midiOutChan; #ifdef WITH_VST jchannel[PATCH_KEY_CHANNEL_PLUGINS] = nl::json::array(); for (ID pid : c.pluginIds) jchannel[PATCH_KEY_CHANNEL_PLUGINS].push_back(pid); #endif j[PATCH_KEY_CHANNELS].push_back(jchannel); } } /* -------------------------------------------------------------------------- */ void modernize_(Patch::Data& patch) { for (Patch::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) 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) { c.pan = G_DEFAULT_PAN; c.waveId = 0; } } } } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ bool Patch::Version::operator==(const Version& o) const { return major == o.major && minor == o.minor && patch == o.patch; } bool Patch::Version::operator<(const Version& o) const { if (major < o.major) return true; if (minor < o.minor) return true; if (patch < o.patch) return true; return false; } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ void Patch::reset() { data = Data(); } /* -------------------------------------------------------------------------- */ bool Patch::write(const std::string& file) const { nl::json j; writeCommons_(data, j); writeColumns_(data, j); writeChannels_(data, j); writeActions_(data, j); writeWaves_(data, j); #ifdef WITH_VST writePlugins_(data, j); #endif std::ofstream ofs(file); if (!ofs.good()) return false; ofs << j; return true; } /* -------------------------------------------------------------------------- */ int Patch::read(const std::string& file, const std::string& basePath) { std::ifstream ifs(file); if (!ifs.good()) return G_FILE_UNREADABLE; nl::json j = nl::json::parse(ifs); if (j[PATCH_KEY_HEADER] != "GIADAPTC") return G_FILE_INVALID; data.version = { static_cast(j[PATCH_KEY_VERSION_MAJOR]), static_cast(j[PATCH_KEY_VERSION_MINOR]), static_cast(j[PATCH_KEY_VERSION_PATCH])}; if (data.version < Version{0, 16, 0}) return G_FILE_UNSUPPORTED; try { readCommons_(data, j); readColumns_(data, j); #ifdef WITH_VST readPlugins_(data, j); #endif readWaves_(data, j, basePath); readActions_(data, j); readChannels_(data, j); modernize_(data); } catch (nl::json::exception& e) { u::log::print("[patch::read] Exception thrown: %s\n", e.what()); return G_FILE_INVALID; } return G_FILE_OK; } } // namespace giada::m giada-0.22.0/src/core/patch.h000066400000000000000000000100421425106661500156320ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_PATCH_H #define G_PATCH_H #include "core/const.h" #include "core/types.h" #include #include #include namespace giada::m { class Patch { public: struct Version { int major = G_VERSION_MAJOR; int minor = G_VERSION_MINOR; int patch = G_VERSION_PATCH; bool operator==(const Version& o) const; bool operator<(const Version& o) const; }; struct Column { ID id; int width; }; struct Channel { ID id; ChannelType type; int height; std::string name; ID columnId; int key; bool mute; bool solo; float volume = G_DEFAULT_VOL; float pan = G_DEFAULT_PAN; bool hasActions; bool armed; bool midiIn; uint32_t midiInKeyPress; uint32_t midiInKeyRel; uint32_t midiInKill; uint32_t midiInArm; uint32_t midiInVolume; uint32_t midiInMute; uint32_t midiInSolo; int midiInFilter; bool midiOutL; uint32_t midiOutLplaying; uint32_t midiOutLmute; uint32_t midiOutLsolo; // sample channel ID waveId = 0; SamplePlayerMode mode; Frame begin; Frame end; Frame shift; bool readActions; float pitch = G_DEFAULT_PITCH; bool inputMonitor; bool overdubProtection; bool midiInVeloAsVol; uint32_t midiInReadActions; uint32_t midiInPitch; // midi channel bool midiOut; int midiOutChan; #ifdef WITH_VST std::vector pluginIds; #endif }; struct Action { ID id; ID channelId; Frame frame; uint32_t event; ID prevId; ID nextId; }; struct Wave { ID id; std::string path; }; #ifdef WITH_VST struct Plugin { ID id; std::string path; bool bypass; std::vector params; // TODO - to be removed in 0.18.0 std::string state; std::vector midiInParams; }; #endif struct Data { Version version; std::string name = G_DEFAULT_PATCH_NAME; int bars = G_DEFAULT_BARS; int beats = G_DEFAULT_BEATS; float bpm = G_DEFAULT_BPM; bool quantize = G_DEFAULT_QUANTIZE; int lastTakeId = 0; int samplerate = G_DEFAULT_SAMPLERATE; bool metronome = false; std::vector columns; std::vector channels; std::vector actions; std::vector waves; #ifdef WITH_VST std::vector plugins; #endif }; /* write Writes patch to file. */ bool write(const std::string& file) const; /* reset Initializes the patch with default values. */ void reset(); /* read Reads patch from file. It takes 'basePath' as parameter for Wave reading. */ int read(const std::string& file, const std::string& basePath); Data data; }; } // namespace giada::m #endif giada-0.22.0/src/core/plugins/000077500000000000000000000000001425106661500160465ustar00rootroot00000000000000giada-0.22.0/src/core/plugins/plugin.cpp000066400000000000000000000205301425106661500200500ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #include "core/plugins/plugin.h" #include "core/const.h" #include "utils/log.h" #include "utils/time.h" #include #include #include namespace giada::m { Plugin::Plugin(ID id, const std::string& UID) : id(id) , valid(false) , onEditorResize(nullptr) , m_plugin(nullptr) , m_UID(UID) , m_hasEditor(false) { } /* -------------------------------------------------------------------------- */ Plugin::Plugin(ID id, std::unique_ptr plugin, std::unique_ptr playHead, double samplerate, int buffersize) : id(id) , valid(true) , onEditorResize(nullptr) , m_plugin(std::move(plugin)) , m_playHead(std::move(playHead)) , 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 during MIDI learning process. */ for (int i = 0; i < m_plugin->getParameters().size(); i++) midiInParams.emplace_back(0x0, i); m_buffer.setSize(G_MAX_IO_CHANS, buffersize); /* Try to set the main bus to the current number of channels. In the future this setup will be performed manually through a proper channel matrix. */ 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); /* 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()); } /* -------------------------------------------------------------------------- */ Plugin::~Plugin() { if (!valid) return; juce::AudioProcessorEditor* e = m_plugin->getActiveEditor(); if (e != nullptr) e->removeComponentListener(this); m_plugin->suspendProcessing(true); m_plugin->releaseResources(); } /* -------------------------------------------------------------------------- */ void Plugin::componentMovedOrResized(juce::Component& c, bool moved, bool /* resized*/) { if (moved) return; if (onEditorResize != nullptr) onEditorResize(c.getWidth(), c.getHeight()); } /* -------------------------------------------------------------------------- */ juce::AudioProcessor::Bus* Plugin::getMainBus(BusType b) const { const bool isInput = static_cast(b); for (int i = 0; i < m_plugin->getBusCount(isInput); i++) if (m_plugin->getBus(isInput, i)->isMain()) return m_plugin->getBus(isInput, i); return nullptr; } int Plugin::countMainOutChannels() const { juce::AudioProcessor::Bus* b = getMainBus(BusType::OUT); assert(b != nullptr); return b->getNumberOfChannels(); } /* -------------------------------------------------------------------------- */ juce::AudioProcessorEditor* Plugin::createEditor() const { juce::AudioProcessorEditor* e = m_plugin->createEditorIfNeeded(); if (e != nullptr) e->addComponentListener(const_cast(this)); return e; } /* -------------------------------------------------------------------------- */ 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); } /* -------------------------------------------------------------------------- */ std::string Plugin::getName() const { if (!valid) return "** invalid **"; return m_plugin->getName().toStdString(); } /* -------------------------------------------------------------------------- */ bool Plugin::isSuspended() const { if (!valid) return false; return m_plugin->isSuspended(); } /* -------------------------------------------------------------------------- */ bool Plugin::isInstrument() const { if (!valid) return false; return m_plugin->acceptsMidi() && m_plugin->getTotalNumInputChannels() == 0; } /* -------------------------------------------------------------------------- */ 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); } /* -------------------------------------------------------------------------- */ const Plugin::Buffer& Plugin::process(const Plugin::Buffer& out, juce::MidiBuffer m) { /* Copy the incoming buffer data into the temporary one. This way FXes will process existing audio data on the private buffer. This is needed later on when merging it back into the incoming buffer. */ m_buffer = out; m_plugin->processBlock(m_buffer, m); return m_buffer; } /* -------------------------------------------------------------------------- */ void Plugin::setState(PluginState state) { m_plugin->setStateInformation(state.getData(), state.getSize()); } /* -------------------------------------------------------------------------- */ int Plugin::getNumPrograms() const { if (!valid) return 0; return m_plugin->getNumPrograms(); } /* -------------------------------------------------------------------------- */ int Plugin::getCurrentProgram() const { if (!valid) return 0; return m_plugin->getCurrentProgram(); } /* -------------------------------------------------------------------------- */ void Plugin::setCurrentProgram(int index) const { if (valid) m_plugin->setCurrentProgram(index); } /* -------------------------------------------------------------------------- */ bool Plugin::hasEditor() const { return m_hasEditor; } /* -------------------------------------------------------------------------- */ std::string Plugin::getProgramName(int index) const { if (!valid) return {}; return m_plugin->getProgramName(index).toStdString(); } /* -------------------------------------------------------------------------- */ std::string Plugin::getParameterName(int index) const { if (!valid) return {}; const int labelSize = 64; return m_plugin->getParameters()[index]->getName(labelSize).toStdString(); } /* -------------------------------------------------------------------------- */ std::string Plugin::getParameterText(int index) const { return m_plugin->getParameters()[index]->getCurrentValueAsText().toStdString(); } /* -------------------------------------------------------------------------- */ std::string Plugin::getParameterLabel(int index) const { return m_plugin->getParameters()[index]->getLabel().toStdString(); } } // namespace giada::m #endif giada-0.22.0/src/core/plugins/plugin.h000066400000000000000000000112651425106661500175220ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #ifndef G_PLUGIN_H #define G_PLUGIN_H #include "core/const.h" #include "core/midiLearnParam.h" #include "core/plugins/pluginHost.h" #include "core/plugins/pluginState.h" #include "deps/juce-config.h" #include #include namespace giada::m { class Plugin : private juce::ComponentListener { public: using Buffer = juce::AudioBuffer; /* Plugin (1) Constructs an invalid plug-in. */ Plugin(ID id, const std::string& UID); /* Plugin (2) Constructs a valid and working plug-in. */ Plugin(ID id, std::unique_ptr, std::unique_ptr, double samplerate, int buffersize); Plugin(const Plugin& o) = delete; Plugin(Plugin&& o) = delete; Plugin& operator=(const Plugin&) = delete; Plugin& operator=(Plugin&&) = delete; ~Plugin(); /* 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; bool isInstrument() 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; PluginState getState() const; juce::AudioProcessorEditor* createEditor() const; /* countMainOutChannels Returns the current channel layout for the main output bus. */ int countMainOutChannels() const; /* process Process the plug-in with audio and MIDI data. The audio buffer is a reference, while the MIDI buffer must be passed by copy: each plug-in must receive its own copy of the event set, so that any attempt to change/clear the MIDI buffer will only modify the local copy. Returns a reference of the local buffer filled with processed data. */ const Buffer& process(const Buffer& b, juce::MidiBuffer m); void setState(PluginState p); void setBypass(bool b); /* id Unique identifier. */ ID id; /* midiInParams A vector of MidiLearnParam's for controlling plug-in parameters with external hardware. */ std::vector midiInParams; /* valid A missing plug-in is loaded anyway, yet marked as 'invalid'. */ bool valid; std::function onEditorResize; private: #ifdef G_OS_WINDOWS /* Fuck... */ #undef IN #undef OUT #endif 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; std::unique_ptr m_plugin; std::unique_ptr m_playHead; Buffer m_buffer; std::atomic m_bypass; /* UID 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; }; } // namespace giada::m #endif #endif // #ifdef WITH_VST giada-0.22.0/src/core/plugins/pluginHost.cpp000066400000000000000000000163611425106661500207150ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #include "core/plugins/pluginHost.h" #include "core/channels/channel.h" #include "core/const.h" #include "core/model/model.h" #include "core/plugins/plugin.h" #include "core/plugins/pluginManager.h" #include "deps/mcl-audio-buffer/src/audioBuffer.hpp" #include "utils/log.h" #include "utils/vector.h" #include #include #include namespace giada::m { PluginHost::Info::Info(const Sequencer& s, int sampleRate) : m_sequencer(s) , m_sampleRate(sampleRate) { } /* -------------------------------------------------------------------------- */ bool PluginHost::Info::getCurrentPosition(CurrentPositionInfo& result) { result.bpm = m_sequencer.getBpm(); result.timeInSamples = m_sequencer.getCurrentFrame(); result.timeInSeconds = m_sequencer.getCurrentSecond(m_sampleRate); result.isPlaying = m_sequencer.isRunning(); return true; } /* -------------------------------------------------------------------------- */ bool PluginHost::Info::canControlTransport() { return false; } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ PluginHost::PluginHost(model::Model& m) : m_model(m) { } /* -------------------------------------------------------------------------- */ void PluginHost::reset(int bufferSize) { freeAllPlugins(); m_audioBuffer.setSize(G_MAX_IO_CHANS, bufferSize); } /* -------------------------------------------------------------------------- */ void PluginHost::processStack(mcl::AudioBuffer& outBuf, const std::vector& plugins, juce::MidiBuffer* events) { assert(outBuf.countFrames() == m_audioBuffer.getNumSamples()); giadaToJuceTempBuf(outBuf); if (events == nullptr) { juce::MidiBuffer dummyEvents; // empty processPlugins(plugins, dummyEvents); } else processPlugins(plugins, *events); juceToGiadaOutBuf(outBuf); } /* -------------------------------------------------------------------------- */ const Plugin& PluginHost::addPlugin(std::unique_ptr p) { m_model.addShared(std::move(p)); return m_model.backShared(); } /* -------------------------------------------------------------------------- */ void PluginHost::swapPlugin(const m::Plugin& p1, const m::Plugin& p2, std::vector& plugins) { std::size_t index1 = u::vector::indexOf(plugins, &p1); std::size_t index2 = u::vector::indexOf(plugins, &p2); std::swap(plugins.at(index1), plugins.at(index2)); } /* -------------------------------------------------------------------------- */ void PluginHost::freePlugin(const m::Plugin& plugin) { m_model.removeShared(plugin); } void PluginHost::freePlugins(const std::vector& plugins) { for (const Plugin* p : plugins) m_model.removeShared(*p); } /* -------------------------------------------------------------------------- */ void PluginHost::freeAllPlugins() { m_model.clearShared(); } /* -------------------------------------------------------------------------- */ void PluginHost::setPluginParameter(ID pluginId, int paramIndex, float value) { m_model.findShared(pluginId)->setParameter(paramIndex, value); } /* -------------------------------------------------------------------------- */ void PluginHost::setPluginProgram(ID pluginId, int programIndex) { m_model.findShared(pluginId)->setCurrentProgram(programIndex); } /* -------------------------------------------------------------------------- */ void PluginHost::toggleBypass(ID pluginId) { Plugin& plugin = *m_model.findShared(pluginId); plugin.setBypass(!plugin.isBypassed()); } /* -------------------------------------------------------------------------- */ void PluginHost::giadaToJuceTempBuf(const mcl::AudioBuffer& outBuf) { assert(outBuf.countChannels() == m_audioBuffer.getNumChannels()); using namespace juce; using Format = AudioData::Format; AudioData::deinterleaveSamples( AudioData::InterleavedSource{outBuf[0], outBuf.countChannels()}, AudioData::NonInterleavedDest{m_audioBuffer.getArrayOfWritePointers(), m_audioBuffer.getNumChannels()}, outBuf.countFrames()); } void PluginHost::juceToGiadaOutBuf(mcl::AudioBuffer& outBuf) const { assert(outBuf.countChannels() == m_audioBuffer.getNumChannels()); using namespace juce; using Format = AudioData::Format; AudioData::interleaveSamples( AudioData::NonInterleavedSource{m_audioBuffer.getArrayOfReadPointers(), m_audioBuffer.getNumChannels()}, AudioData::InterleavedDest{outBuf[0], outBuf.countChannels()}, outBuf.countFrames()); } /* -------------------------------------------------------------------------- */ void PluginHost::processPlugins(const std::vector& plugins, juce::MidiBuffer& events) { for (Plugin* p : plugins) { if (!p->valid || p->isSuspended() || p->isBypassed()) continue; processPlugin(p, events); } events.clear(); } /* -------------------------------------------------------------------------- */ void PluginHost::processPlugin(Plugin* p, const juce::MidiBuffer& events) { const Plugin::Buffer& pluginBuffer = p->process(m_audioBuffer, events); const bool isInstrument = p->isInstrument(); /* Merge the plugin buffer back into the local one. Special care is needed if audio channels mismatch. */ for (int i = 0, j = 0; i < m_audioBuffer.getNumChannels(); i++) { /* If instrument (i.e. a plug-in that accepts MIDI and produces audio out of it), SUM the local working buffer to the main one. This allows multiple plug-in instruments to play simultaneously on a given set of MIDI events. If it's a normal FX instead (!isInstrument), the local working buffer is simply copied over the main one. */ if (isInstrument) m_audioBuffer.addFrom(i, 0, pluginBuffer, j, 0, pluginBuffer.getNumSamples()); else m_audioBuffer.copyFrom(i, 0, pluginBuffer, j, 0, pluginBuffer.getNumSamples()); if (i < p->countMainOutChannels() - 1) j++; } } } // namespace giada::m #endif // #ifdef WITH_VST giada-0.22.0/src/core/plugins/pluginHost.h000066400000000000000000000066101425106661500203560ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #ifndef G_PLUGIN_HOST_H #define G_PLUGIN_HOST_H #include "core/types.h" #include "deps/juce-config.h" #include #include namespace mcl { class AudioBuffer; } namespace giada::m { class Plugin; } namespace giada::m::model { class Model; } namespace giada::m { class Sequencer; class PluginHost final { public: class Info final : public juce::AudioPlayHead { public: Info(const Sequencer&, int sampleRate); bool getCurrentPosition(CurrentPositionInfo& result) override; bool canControlTransport() override; private: const Sequencer& m_sequencer; int m_sampleRate; }; PluginHost(model::Model&); /* reset Brings everything back to the initial state. */ void reset(int bufferSize); /* addPlugin Loads a new plugin into memory. Returns a reference to the newly created object. */ const Plugin& addPlugin(std::unique_ptr p); /* processStack Applies the fx list to the buffer. */ void processStack(mcl::AudioBuffer& outBuf, const std::vector& plugins, juce::MidiBuffer* events = nullptr); /* swapPlugin Swaps plug-in 1 with plug-in 2 in the plug-in vector. */ void swapPlugin(const m::Plugin& p1, const m::Plugin& p2, std::vector& plugins); /* freePlugin. Unloads plugin from memory. */ void freePlugin(const m::Plugin& plugin); /* freePlugins Unloads multiple plugins. Useful when freeing or deleting a channel. */ void freePlugins(const std::vector& plugins); /* freeAllPlugins Just deletes everything. */ void freeAllPlugins(); void setPluginParameter(ID pluginId, int paramIndex, float value); void setPluginProgram(ID pluginId, int programIndex); void toggleBypass(ID pluginId); private: /* giadaToJuceTempBuf Copies the Giada buffer 'outBuf' to the private JUCE buffer for local processing. */ void giadaToJuceTempBuf(const mcl::AudioBuffer& outBuf); /* juceToGiadaOutBuf Copies the private JUCE buffer to Giada buffer 'outBuf'. */ void juceToGiadaOutBuf(mcl::AudioBuffer& outBuf) const; void processPlugins(const std::vector&, juce::MidiBuffer& events); void processPlugin(Plugin*, const juce::MidiBuffer& events); model::Model& m_model; juce::AudioBuffer m_audioBuffer; }; } // namespace giada::m #endif #endif // #ifdef WITH_VST giada-0.22.0/src/core/plugins/pluginManager.cpp000066400000000000000000000232251425106661500213470ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #include "core/plugins/pluginManager.h" #include "core/const.h" #include "core/model/model.h" #include "core/patch.h" #include "core/plugins/plugin.h" #include "utils/fs.h" #include "utils/log.h" #include "utils/string.h" #include #include #include namespace giada::m { void PluginManager::reset(SortMethod sortMethod) { m_pluginId = IdManager(); m_missingPlugins = false; m_unknownPluginList.clear(); if (m_formatManager.getNumFormats() == 0) // Must be called only once m_formatManager.addDefaultFormats(); loadList(u::fs::join(u::fs::getHomePath(), "plugins.xml")); sortPlugins(sortMethod); } /* -------------------------------------------------------------------------- */ int PluginManager::scanDirs(const std::string& dirs, const std::function& cb) { u::log::print("[pluginManager::scanDir] requested directories: '%s'\n", dirs); u::log::print("[pluginManager::scanDir] currently known plug-ins: %d\n", m_knownPluginList.getNumTypes()); m_knownPluginList.clear(); // clear up previous plugins std::vector dirVec = u::string::split(dirs, ";"); juce::FileSearchPath searchPath; for (const std::string& dir : dirVec) searchPath.add(juce::File(dir)); for (int i = 0; i < m_formatManager.getNumFormats(); i++) { juce::PluginDirectoryScanner scanner(m_knownPluginList, *m_formatManager.getFormat(i), searchPath, /*recursive=*/true, juce::File()); juce::String name; while (scanner.scanNextFile(false, name)) { u::log::print("[pluginManager::scanDir] scanning '%s'\n", name.toRawUTF8()); cb(scanner.getProgress()); } } u::log::print("[pluginManager::scanDir] %d plugin(s) found\n", m_knownPluginList.getNumTypes()); return m_knownPluginList.getNumTypes(); } /* -------------------------------------------------------------------------- */ bool PluginManager::saveList(const std::string& filepath) const { bool out = m_knownPluginList.createXml()->writeTo(juce::File(filepath)); if (!out) u::log::print("[pluginManager::saveList] unable to save plugin list to %s\n", filepath); return out; } /* -------------------------------------------------------------------------- */ bool PluginManager::loadList(const std::string& filepath) { std::unique_ptr elem(juce::XmlDocument::parse(juce::File(filepath))); if (elem == nullptr) return false; m_knownPluginList.recreateFromXml(*elem); return true; } /* -------------------------------------------------------------------------- */ std::unique_ptr PluginManager::makePlugin(const std::string& pid, int sampleRate, int bufferSize, const Sequencer& sequencer, ID id) { /* Plug-in ID generator is updated anyway, as we store Plugin objects also if they are in an invalid state. */ m_pluginId.set(id); const std::unique_ptr pd = m_knownPluginList.getTypeForIdentifierString(pid); if (pd == nullptr) { u::log::print("[pluginManager::makePlugin] no plugin found with pid=%s!\n", pid); return makeInvalidPlugin(pid, id); } juce::String error; std::unique_ptr pi = m_formatManager.createPluginInstance(*pd, sampleRate, bufferSize, error); if (pi == nullptr) { u::log::print("[pluginManager::makePlugin] unable to create instance with pid=%s! Error: %s\n", pid, error.toStdString()); return makeInvalidPlugin(pid, id); } u::log::print("[pluginManager::makePlugin] plugin instance with pid=%s created\n", pid); return std::make_unique( m_pluginId.generate(id), std::move(pi), std::make_unique(sequencer, sampleRate), sampleRate, bufferSize); } /* -------------------------------------------------------------------------- */ std::unique_ptr PluginManager::makePlugin(int index, int sampleRate, int bufferSize, const Sequencer& sequencer) { juce::PluginDescription pd = m_knownPluginList.getTypes()[index]; if (pd.uniqueId == 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(), sampleRate, bufferSize, sequencer); } /* -------------------------------------------------------------------------- */ std::unique_ptr PluginManager::makePlugin(const Plugin& src, int sampleRate, int bufferSize, const Sequencer& sequencer) { std::unique_ptr p = makePlugin(src.getUniqueId(), sampleRate, bufferSize, sequencer); for (int i = 0; i < src.getNumParameters(); i++) p->setParameter(i, src.getParameter(i)); return p; } /* -------------------------------------------------------------------------- */ const Patch::Plugin PluginManager::serializePlugin(const Plugin& p) const { Patch::Plugin pp; pp.id = p.id; pp.path = p.getUniqueId(); pp.bypass = p.isBypassed(); pp.state = p.getState().asBase64(); for (const MidiLearnParam& param : p.midiInParams) pp.midiInParams.push_back(param.getValue()); return pp; } /* -------------------------------------------------------------------------- */ std::unique_ptr PluginManager::deserializePlugin(const Patch::Plugin& p, int sampleRate, int bufferSize, const Sequencer& sequencer) { std::unique_ptr plugin = makePlugin(p.path, sampleRate, bufferSize, sequencer, p.id); if (!plugin->valid) return plugin; // Return invalid version plugin->setBypass(p.bypass); 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()) { plugin->midiInParams.clear(); std::size_t paramIndex = 0; for (uint32_t midiInParam : p.midiInParams) plugin->midiInParams.emplace_back(midiInParam, paramIndex++); } return plugin; } /* -------------------------------------------------------------------------- */ std::vector PluginManager::hydratePlugins(std::vector pluginIds, model::Model& model) { std::vector out; for (ID id : pluginIds) { Plugin* plugin = model.findShared(id); if (plugin != nullptr) out.push_back(plugin); } return out; } /* -------------------------------------------------------------------------- */ int PluginManager::countAvailablePlugins() const { return m_knownPluginList.getNumTypes(); } /* -------------------------------------------------------------------------- */ std::vector PluginManager::getPluginsInfo() const { std::vector out; for (int i = 0; i < m_knownPluginList.getNumTypes(); i++) { juce::PluginDescription pd = m_knownPluginList.getTypes()[i]; PluginInfo pi; pi.uid = pd.fileOrIdentifier.toStdString(); pi.name = pd.descriptiveName.toStdString(); pi.category = pd.category.toStdString(); pi.manufacturerName = pd.manufacturerName.toStdString(); pi.format = pd.pluginFormatName.toStdString(); pi.isInstrument = pd.isInstrument; pi.exists = m_formatManager.doesPluginStillExist(*m_knownPluginList.getTypeForFile(pi.uid)); pi.isKnown = true; out.push_back(pi); } for (const std::string& uid : m_unknownPluginList) { PluginInfo pi; pi.uid = uid; pi.isInstrument = false; pi.exists = false; pi.isKnown = false; out.push_back(pi); } return out; } /* -------------------------------------------------------------------------- */ bool PluginManager::hasMissingPlugins() const { return m_missingPlugins; } /* -------------------------------------------------------------------------- */ void PluginManager::sortPlugins(SortMethod method) { switch (method) { case SortMethod::NAME: m_knownPluginList.sort(juce::KnownPluginList::SortMethod::sortAlphabetically, true); break; case SortMethod::CATEGORY: m_knownPluginList.sort(juce::KnownPluginList::SortMethod::sortByCategory, true); break; case SortMethod::MANUFACTURER: m_knownPluginList.sort(juce::KnownPluginList::SortMethod::sortByManufacturer, true); break; case SortMethod::FORMAT: m_knownPluginList.sort(juce::KnownPluginList::SortMethod::sortByFormat, true); break; } } /* -------------------------------------------------------------------------- */ std::unique_ptr PluginManager::makeInvalidPlugin(const std::string& pid, ID id) { m_missingPlugins = true; m_unknownPluginList.push_back(pid); return std::make_unique(m_pluginId.generate(id), pid); // Invalid plug-in } } // namespace giada::m #endif // #ifdef WITH_VST giada-0.22.0/src/core/plugins/pluginManager.h000066400000000000000000000100461425106661500210110ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #ifndef G_PLUGIN_MANAGER_H #define G_PLUGIN_MANAGER_H #include "core/idManager.h" #include "core/patch.h" #include "deps/juce-config.h" #include "plugin.h" #include namespace giada::m::patch { struct Plugin; struct Version; } // namespace giada::m::patch namespace giada::m::model { class Model; } namespace giada::m { class Sequencer; class PluginManager final { public: enum class SortMethod : int { NAME = 0, CATEGORY, MANUFACTURER, FORMAT }; struct PluginInfo { std::string uid; std::string name; std::string category; std::string manufacturerName; std::string format; bool isInstrument; bool exists; bool isKnown; }; /* getPluginsInfo Returns a vector of PluginInfo objects containing all plug-ins, known and unknown, scanned so far. */ std::vector getPluginsInfo() const; /* hasMissingPlugins True if some plug-ins have been marked as missing during the initial scan. */ bool hasMissingPlugins() const; /* countAvailablePlugins Returns how many plug-ins are ready and available for usage. */ int countAvailablePlugins() const; /* reset Brings everything back to the initial state. */ void reset(SortMethod); /* scanDirs Parses plugin directories (semicolon-separated) and store list in knownPluginList. The callback is called on each plugin found. Used to update the main window from the GUI thread. */ int scanDirs(const std::string& paths, const std::function& cb); /* (save|load)List (Save|Load) knownPluginList (in|from) an XML file. */ bool saveList(const std::string& path) const; bool loadList(const std::string& path); std::unique_ptr makePlugin(const std::string& pid, int sampleRate, int bufferSize, const Sequencer&, ID id = 0); std::unique_ptr makePlugin(int index, int sampleRate, int bufferSize, const Sequencer&); std::unique_ptr makePlugin(const Plugin& other, int sampleRate, int bufferSize, const Sequencer&); /* (de)serializePlugin Transforms patch data into a Plugin object and vice versa. */ const Patch::Plugin serializePlugin(const Plugin& p) const; std::unique_ptr deserializePlugin(const Patch::Plugin&, int sampleRate, int bufferSize, const Sequencer&); std::vector hydratePlugins(std::vector pluginIds, model::Model& model); void sortPlugins(SortMethod sortMethod); private: std::unique_ptr makeInvalidPlugin(const std::string& pid, ID id); IdManager m_pluginId; /* formatManager Plugin format manager. */ juce::AudioPluginFormatManager m_formatManager; /* knownPuginList List of known (i.e. scanned) plugins. */ juce::KnownPluginList m_knownPluginList; /* unknownPluginList List of unrecognized plugins found in a patch. */ std::vector m_unknownPluginList; /* missingPlugins If some plugins from any stack are missing. */ bool m_missingPlugins; }; } // namespace giada::m #endif #endif // #ifdef WITH_VST giada-0.22.0/src/core/plugins/pluginState.cpp000066400000000000000000000037511425106661500210570ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #include "pluginState.h" #include "core/const.h" #include 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); 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 { return m_data.getData(); } size_t PluginState::getSize() const { return m_data.getSize(); } } // namespace giada::m #endif // #ifdef WITH_VST giada-0.22.0/src/core/plugins/pluginState.h000066400000000000000000000031511425106661500205160ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #ifndef G_PLUGIN_STATE_H #define G_PLUGIN_STATE_H #include "deps/juce-config.h" #include #include namespace giada::m { class PluginState { public: PluginState() = default; // Invalid state PluginState(juce::MemoryBlock&& data); PluginState(const std::string& base64); std::string asBase64() const; const void* getData() const; size_t getSize() const; private: juce::MemoryBlock m_data; }; } // namespace giada::m #endif #endif // #ifdef WITH_VST giada-0.22.0/src/core/quantizer.cpp000066400000000000000000000045301425106661500171150ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "quantizer.h" #include namespace giada::m { void Quantizer::trigger(int id) { assert(m_callbacks.count(id) > 0); // Make sure id exists m_performId.store(id); } /* -------------------------------------------------------------------------- */ void Quantizer::schedule(int id, std::function f) { m_callbacks[id] = f; } /* -------------------------------------------------------------------------- */ void Quantizer::advance(Range block, Frame quantizerStep) { /* Nothing to do if there's no action to perform. */ const int pid = m_performId.load(); if (pid == -1) return; assert(m_callbacks.count(pid) > 0); 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. continue; m_callbacks.at(pid)(local); m_performId.store(-1); return; } } /* -------------------------------------------------------------------------- */ void Quantizer::clear() { m_performId.store(-1); } /* -------------------------------------------------------------------------- */ bool Quantizer::hasBeenTriggered() const { return m_performId.load() != -1; } } // namespace giada::mgiada-0.22.0/src/core/quantizer.h000066400000000000000000000043721425106661500165660ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_QUANTIZER_H #define G_QUANTIZER_H #include "core/const.h" #include "core/range.h" #include "core/types.h" #include "core/weakAtomic.h" #include #include 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 schedule(int id, std::function); /* trigger Triggers the function in slot 'id'. Might start right away, or at the end of the quantization step. */ void trigger(int id); /* advance Computes the internal state. Wants a range of frames [currentFrame, currentFrame + bufferSize) and a quantization step. Call this function on each block. */ void advance(Range block, Frame quantizerStep); /* clear Disables quantized operations in progress, if any. */ void clear(); /* hasBeenTriggered True if a quantizer function has been triggered(). */ bool hasBeenTriggered() const; private: std::map> m_callbacks; WeakAtomic m_performId = -1; }; } // namespace giada::m #endif giada-0.22.0/src/core/queue.h000066400000000000000000000042621425106661500156660ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2019 Giovanni A. Zuliani | Monocasual * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_QUEUE_H #define G_QUEUE_H #include #include #include namespace giada::m { /* Queue Single producer, single consumer lock-free queue. */ template class Queue { public: Queue() : m_head(0) , m_tail(0) { } Queue(const Queue&) = delete; Queue(Queue&&) = delete; Queue& operator=(const Queue&) = delete; Queue& operator=(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 m_data; std::atomic m_head; std::atomic m_tail; }; } // namespace giada::m #endif giada-0.22.0/src/core/range.h000066400000000000000000000030701425106661500156320ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_RANGE_H #define G_RANGE_H #include namespace giada { template class Range { public: 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 getLength() const { return m_b - m_a; } bool contains(T t) const { return t >= m_a && t < m_b; } private: T m_a; T m_b; }; } // namespace giada #endif giada-0.22.0/src/core/recorder.cpp000066400000000000000000000147061425106661500167060ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/recorder.h" #include "core/mixerHandler.h" #include "core/model/model.h" #include "core/sequencer.h" #include "core/types.h" #include "src/core/actions/actionRecorder.h" #include "src/core/actions/actions.h" namespace giada::m { Recorder::Recorder(model::Model& m, Sequencer& s, MixerHandler& mh) : m_model(m) , m_sequencer(s) , m_mixerHandler(mh) { } /* -------------------------------------------------------------------------- */ bool Recorder::isRecording() const { return isRecordingAction() || isRecordingInput(); } bool Recorder::isRecordingAction() const { return m_model.get().recorder.a_isRecordingAction(); } bool Recorder::isRecordingInput() const { return m_model.get().recorder.a_isRecordingInput(); } /* -------------------------------------------------------------------------- */ void Recorder::prepareActionRec(RecTriggerMode mode) { if (mode == RecTriggerMode::NORMAL) { startActionRec(); m_sequencer.setStatus(SeqStatus::RUNNING); G_DEBUG("Start action rec, NORMAL mode"); } else { // RecTriggerMode::SIGNAL m_sequencer.setStatus(SeqStatus::WAITING); G_DEBUG("Start action rec, SIGNAL mode (waiting for signal from Midi Dispatcher...)"); } } /* -------------------------------------------------------------------------- */ void Recorder::stopActionRec(ActionRecorder& actionRecorder) { setRecordingAction(false); /* If you stop the Action Recorder in SIGNAL mode before any actual recording: just clean up everything and return. */ if (m_sequencer.getStatus() == SeqStatus::WAITING) { m_sequencer.setStatus(SeqStatus::STOPPED); return; } std::unordered_set channels = actionRecorder.consolidate(); /* Enable reading actions for Channels that have just been filled with actions. Start reading right away, without checking whether conf::treatRecsAsLoops is enabled or not. Same thing for MIDI channels. */ for (ID id : channels) { Channel& ch = m_model.get().getChannel(id); ch.shared->readActions.store(true); ch.shared->recStatus.store(ChannelStatus::PLAY); if (ch.type == ChannelType::MIDI) ch.shared->playStatus.store(ChannelStatus::PLAY); } m_model.swap(model::SwapType::HARD); } /* -------------------------------------------------------------------------- */ bool Recorder::prepareInputRec(RecTriggerMode triggerMode, InputRecMode inputMode) { if (inputMode == InputRecMode::FREE) m_sequencer.rewind(); if (triggerMode == RecTriggerMode::NORMAL) { startInputRec(); m_sequencer.setStatus(SeqStatus::RUNNING); G_DEBUG("Start input rec, NORMAL mode"); } else { m_sequencer.setStatus(SeqStatus::WAITING); G_DEBUG("Start input rec, SIGNAL mode (waiting for signal from Mixer...)"); } return true; } /* -------------------------------------------------------------------------- */ void Recorder::stopInputRec(InputRecMode recMode, int sampleRate) { setRecordingInput(false); Frame recordedFrames = m_mixerHandler.stopInputRec(); /* When recording in RIGID mode, the amount of recorded frames is always equal to the current loop length. */ if (recMode == InputRecMode::RIGID) recordedFrames = m_sequencer.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 (m_sequencer.getStatus() == SeqStatus::WAITING) { m_sequencer.setStatus(SeqStatus::STOPPED); return; } /* Finalize recordings. InputRecMode::FREE requires some adjustments. */ m_mixerHandler.finalizeInputRec(recordedFrames, m_sequencer.getCurrentFrame()); if (recMode == InputRecMode::FREE) { m_sequencer.rewind(); m_sequencer.setBpm(m_sequencer.calcBpmFromRec(recordedFrames, sampleRate), sampleRate); } } /* -------------------------------------------------------------------------- */ bool Recorder::canEnableRecOnSignal() const { return !m_sequencer.isRunning(); } bool Recorder::canEnableFreeInputRec() const { return !m_mixerHandler.hasAudioData(); } /* -------------------------------------------------------------------------- */ bool Recorder::canRecordActions() const { return isRecordingAction() && m_sequencer.isRunning() && !isRecordingInput(); } /* -------------------------------------------------------------------------- */ void Recorder::setRecordingAction(bool v) { m_model.get().recorder.a_setRecordingAction(v); } void Recorder::setRecordingInput(bool v) { m_model.get().recorder.a_setRecordingInput(v); } /* -------------------------------------------------------------------------- */ void Recorder::startActionRec() { setRecordingAction(true); } /* -------------------------------------------------------------------------- */ void Recorder::startActionRecOnCallback() { if (m_sequencer.getStatus() != SeqStatus::WAITING) return; startActionRec(); m_sequencer.setStatus(SeqStatus::RUNNING); } /* -------------------------------------------------------------------------- */ void Recorder::startInputRec() { /* Start recording from the current frame, not the beginning. */ m_mixerHandler.startInputRec(m_sequencer.getCurrentFrame()); setRecordingInput(true); } /* -------------------------------------------------------------------------- */ void Recorder::startInputRecOnCallback() { if (m_sequencer.getStatus() != SeqStatus::WAITING) return; startInputRec(); m_sequencer.setStatus(SeqStatus::RUNNING); } } // namespace giada::mgiada-0.22.0/src/core/recorder.h000066400000000000000000000047211425106661500163470ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_REC_MANAGER_H #define G_REC_MANAGER_H #include "core/types.h" namespace giada::m::model { class Model; } namespace giada::m { class ActionRecorder; class MixerHandler; class Sequencer; class Recorder final { public: Recorder(model::Model&, Sequencer&, MixerHandler&); bool isRecording() const; bool isRecordingAction() const; bool isRecordingInput() const; /* 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() const; /* 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() const; /* canRecordActions True if actions are recordable right now. */ bool canRecordActions() const; void prepareActionRec(RecTriggerMode); void startActionRec(); void startActionRecOnCallback(); void stopActionRec(ActionRecorder&); bool prepareInputRec(RecTriggerMode, InputRecMode); void startInputRec(); void startInputRecOnCallback(); void stopInputRec(InputRecMode, int sampleRate); private: void setRecordingAction(bool v); void setRecordingInput(bool v); model::Model& m_model; Sequencer& m_sequencer; MixerHandler& m_mixerHandler; }; } // namespace giada::m #endif giada-0.22.0/src/core/resampler.cpp000066400000000000000000000110111425106661500170550ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/resampler.h" #include #include #include #include #include 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(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(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 giada-0.22.0/src/core/resampler.h000066400000000000000000000054231425106661500165340ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_RESAMPLER_H #define G_RESAMPLER_H #include #include 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 #endifgiada-0.22.0/src/core/ringBuffer.h000066400000000000000000000045611425106661500166350ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_RING_BUFFER_H #define G_RING_BUFFER_H #include #include namespace giada { /* RingBuffer A non-thread-safe, fixed-size ring buffer implementation. It grows from 0 to S, then items are overwritten starting from position 0. */ template class RingBuffer { public: using iterator = typename std::array::iterator; using const_iterator = typename std::array::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; } const_iterator cbegin() const { return m_data.begin(); } const_iterator cend() const { return m_data.begin() + m_end; } void clear() { m_data.fill({}); m_index = 0; m_end = 0; } 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 } std::size_t size() const noexcept { return m_end; } private: std::array m_data; std::size_t m_index = 0; std::size_t m_end = 0; }; } // namespace giada #endif giada-0.22.0/src/core/sequencer.cpp000066400000000000000000000271661425106661500170770ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/sequencer.h" #include "core/actions/actionRecorder.h" #include "core/jackTransport.h" #include "core/kernelAudio.h" #include "core/metronome.h" #include "core/model/model.h" #include "core/quantizer.h" #include "core/synchronizer.h" #include "utils/log.h" #include "utils/math.h" namespace giada::m { namespace { constexpr int Q_ACTION_REWIND = 0; } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ Sequencer::Sequencer(model::Model& m, Synchronizer& s, JackTransport& j) : onAboutStart(nullptr) , onAboutStop(nullptr) , m_model(m) , m_synchronizer(s) , m_jackTransport(j) , m_quantizerStep(1) { quantizer.schedule(Q_ACTION_REWIND, [this](Frame delta) { rewindQ(delta); }); } /* -------------------------------------------------------------------------- */ bool Sequencer::canQuantize() const { return m_model.get().sequencer.canQuantize(); } bool Sequencer::isRunning() const { return m_model.get().sequencer.isRunning(); } bool Sequencer::isActive() const { return m_model.get().sequencer.isActive(); } bool Sequencer::isOnBar() const { return m_model.get().sequencer.a_isOnBar(); } bool Sequencer::isOnBeat() const { return m_model.get().sequencer.a_isOnBeat(); } bool Sequencer::isOnFirstBeat() const { return m_model.get().sequencer.a_isOnFirstBeat(); } float Sequencer::getBpm() const { return m_model.get().sequencer.bpm; } int Sequencer::getBeats() const { return m_model.get().sequencer.beats; } int Sequencer::getBars() const { return m_model.get().sequencer.bars; } int Sequencer::getCurrentBeat() const { return m_model.get().sequencer.a_getCurrentBeat(); } Frame Sequencer::getCurrentFrame() const { return m_model.get().sequencer.a_getCurrentFrame(); } Frame Sequencer::getCurrentFrameQuantized() const { return quantize(getCurrentFrame()); } float Sequencer::getCurrentSecond(int sampleRate) const { return getCurrentFrame() / static_cast(sampleRate); } Frame Sequencer::getFramesInBar() const { return m_model.get().sequencer.framesInBar; } Frame Sequencer::getFramesInBeat() const { return m_model.get().sequencer.framesInBeat; } Frame Sequencer::getFramesInLoop() const { return m_model.get().sequencer.framesInLoop; } Frame Sequencer::getFramesInSeq() const { return m_model.get().sequencer.framesInSeq; } int Sequencer::getQuantizerValue() const { return m_model.get().sequencer.quantize; } int Sequencer::getQuantizerStep() const { return m_quantizerStep; } SeqStatus Sequencer::getStatus() const { return m_model.get().sequencer.status; } /* -------------------------------------------------------------------------- */ Frame Sequencer::getMaxFramesInLoop(int sampleRate) const { return (sampleRate * (60.0f / G_MIN_BPM)) * getBeats(); } /* -------------------------------------------------------------------------- */ float Sequencer::calcBpmFromRec(Frame recordedFrames, int sampleRate) const { return (60.0f * getBeats()) / (recordedFrames / static_cast(sampleRate)); } /* -------------------------------------------------------------------------- */ Frame Sequencer::quantize(Frame f) const { if (!canQuantize()) return f; return u::math::quantize(f, m_quantizerStep) % getFramesInLoop(); // No overflow } /* -------------------------------------------------------------------------- */ void Sequencer::reset(int sampleRate) { model::Sequencer& s = m_model.get().sequencer; s.bars = G_DEFAULT_BARS; s.beats = G_DEFAULT_BEATS; s.bpm = G_DEFAULT_BPM; s.quantize = G_DEFAULT_QUANTIZE; recomputeFrames(sampleRate); // Model swap is done here, no need to call it twice rewind(); } /* -------------------------------------------------------------------------- */ void Sequencer::react(const EventDispatcher::EventBuffer& events, int sampleRate) { for (const EventDispatcher::Event& e : events) { switch (e.type) { case EventDispatcher::EventType::SEQUENCER_START: if (!m_jackTransport.start()) rawStart(); break; case EventDispatcher::EventType::SEQUENCER_STOP: if (!m_jackTransport.stop()) rawStop(); break; case EventDispatcher::EventType::SEQUENCER_REWIND: if (!m_jackTransport.setPosition(0)) rawRewind(); break; #ifdef WITH_AUDIO_JACK case EventDispatcher::EventType::SEQUENCER_START_JACK: rawStart(); break; case EventDispatcher::EventType::SEQUENCER_STOP_JACK: rawStop(); break; case EventDispatcher::EventType::SEQUENCER_REWIND_JACK: rawRewind(); break; case EventDispatcher::EventType::SEQUENCER_BPM_JACK: rawSetBpm(std::get(e.data), sampleRate); break; #endif default: break; } } } /* -------------------------------------------------------------------------- */ const Sequencer::EventBuffer& Sequencer::advance(Frame bufferSize, const ActionRecorder& actionRecorder) { m_eventBuffer.clear(); const model::Sequencer& sequencer = m_model.get().sequencer; const Frame start = sequencer.a_getCurrentFrame(); const Frame end = start + bufferSize; const Frame framesInLoop = sequencer.framesInLoop; const Frame framesInBar = sequencer.framesInBar; const Frame framesInBeat = sequencer.framesInBeat; const Frame nextFrame = end % framesInLoop; const int nextBeat = nextFrame / framesInBeat; /* Process events in the current block. */ for (Frame i = start, local = 0; i < end; i++, local++) { Frame global = i % framesInLoop; // wraps around 'framesInLoop' if (global == 0) { m_eventBuffer.push_back({EventType::FIRST_BEAT, global, local}); m_metronome.trigger(Metronome::Click::BEAT, local); } else if (global % framesInBar == 0) { m_eventBuffer.push_back({EventType::BAR, global, local}); m_metronome.trigger(Metronome::Click::BAR, local); } else if (global % framesInBeat == 0) { m_metronome.trigger(Metronome::Click::BEAT, local); } const std::vector* as = actionRecorder.getActionsOnFrame(global); if (as != nullptr) m_eventBuffer.push_back({EventType::ACTIONS, global, local, as}); } /* Advance this and quantizer after the event parsing. */ sequencer.a_setCurrentFrame(nextFrame); sequencer.a_setCurrentBeat(nextBeat); quantizer.advance(Range(start, end), getQuantizerStep()); return m_eventBuffer; } /* -------------------------------------------------------------------------- */ void Sequencer::render(mcl::AudioBuffer& outBuf) { if (m_metronome.running) m_metronome.render(outBuf); } /* -------------------------------------------------------------------------- */ void Sequencer::rawStart() { assert(onAboutStart != nullptr); const SeqStatus status = getStatus(); onAboutStart(status); switch (status) { case SeqStatus::STOPPED: setStatus(SeqStatus::RUNNING); break; case SeqStatus::WAITING: setStatus(SeqStatus::RUNNING); break; default: break; } } /* -------------------------------------------------------------------------- */ void Sequencer::rawStop() { assert(onAboutStop != nullptr); onAboutStop(); setStatus(SeqStatus::STOPPED); } /* -------------------------------------------------------------------------- */ void Sequencer::rawRewind() { if (canQuantize()) quantizer.trigger(Q_ACTION_REWIND); else rewindQ(/*delta=*/0); } /* -------------------------------------------------------------------------- */ void Sequencer::rewind() { const model::Sequencer& c = m_model.get().sequencer; c.a_setCurrentFrame(0); c.a_setCurrentBeat(0); } /* -------------------------------------------------------------------------- */ bool Sequencer::isMetronomeOn() const { return m_metronome.running; } void Sequencer::toggleMetronome() { m_metronome.running = !m_metronome.running; } void Sequencer::setMetronome(bool v) { m_metronome.running = v; } /* -------------------------------------------------------------------------- */ void Sequencer::rewindQ(Frame delta) { rewind(); m_eventBuffer.push_back({EventType::REWIND, 0, delta}); } /* -------------------------------------------------------------------------- */ void Sequencer::recomputeFrames(int sampleRate) { model::Sequencer& s = m_model.get().sequencer; s.framesInLoop = static_cast((sampleRate * (60.0f / s.bpm)) * s.beats); s.framesInBar = static_cast(s.framesInLoop / (float)s.bars); s.framesInBeat = static_cast(s.framesInLoop / (float)s.beats); s.framesInSeq = s.framesInBeat * G_MAX_BEATS; if (s.quantize != 0) m_quantizerStep = s.framesInBeat / s.quantize; m_model.swap(model::SwapType::NONE); } /* -------------------------------------------------------------------------- */ void Sequencer::setBpm(float b, int sampleRate) { b = std::clamp(b, G_MIN_BPM, G_MAX_BPM); /* If JACK is being used, let it handle the bpm change. */ if (!m_jackTransport.setBpm(b)) rawSetBpm(b, sampleRate); } /* -------------------------------------------------------------------------- */ void Sequencer::rawSetBpm(float v, int sampleRate) { assert(onBpmChange != nullptr); const float oldVal = m_model.get().sequencer.bpm; const float newVal = v; m_model.get().sequencer.bpm = newVal; recomputeFrames(sampleRate); m_model.swap(model::SwapType::HARD); onBpmChange(oldVal, newVal, m_quantizerStep); u::log::print("[clock::rawSetBpm] Bpm changed to %f\n", newVal); } /* -------------------------------------------------------------------------- */ void Sequencer::setBeats(int newBeats, int newBars, int sampleRate) { newBeats = std::clamp(newBeats, 1, G_MAX_BEATS); newBars = std::clamp(newBars, 1, newBeats); // Bars cannot be greater than beats m_model.get().sequencer.beats = newBeats; m_model.get().sequencer.bars = newBars; recomputeFrames(sampleRate); m_model.swap(model::SwapType::HARD); } /* -------------------------------------------------------------------------- */ void Sequencer::setQuantize(int q, int sampleRate) { m_model.get().sequencer.quantize = q; recomputeFrames(sampleRate); m_model.swap(model::SwapType::HARD); } /* -------------------------------------------------------------------------- */ void Sequencer::setStatus(SeqStatus s) { m_model.get().sequencer.status = s; m_model.swap(model::SwapType::SOFT); /* Additional things to do when the status changes. */ switch (s) { case SeqStatus::WAITING: rewind(); m_synchronizer.sendMIDIrewind(); break; case SeqStatus::STOPPED: m_synchronizer.sendMIDIstop(); break; case SeqStatus::RUNNING: m_synchronizer.sendMIDIstart(); break; default: break; } } } // namespace giada::mgiada-0.22.0/src/core/sequencer.h000066400000000000000000000132101425106661500165250ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_SEQUENCER_H #define G_SEQUENCER_H #include "core/eventDispatcher.h" #include "core/metronome.h" #include "core/quantizer.h" #include namespace mcl { class AudioBuffer; } namespace giada::m::model { class Model; } namespace giada::m { class JackTransport; class ActionRecorder; class Synchronizer; class Sequencer final { public: enum class EventType { NONE, FIRST_BEAT, BAR, REWIND, ACTIONS }; struct Event { EventType type = EventType::NONE; Frame global = 0; Frame delta = 0; const std::vector* actions = nullptr; }; using EventBuffer = RingBuffer; Sequencer(model::Model&, Synchronizer&, JackTransport&); /* canQuantize Tells whether the quantizer value is > 0 and the sequencer is running. */ bool canQuantize() const; /* isRunning When sequencer is actually moving forward, i.e. SeqStatus == RUNNING. */ bool isRunning() const; /* isActive Sequencer is enabled, but might be in wait mode, i.e. SeqStatus == RUNNING or SeqStatus == WAITING. */ bool isActive() const; bool isOnBeat() const; bool isOnBar() const; bool isOnFirstBeat() const; bool isMetronomeOn() const; float getBpm() const; int getBeats() const; int getBars() const; int getCurrentBeat() const; Frame getCurrentFrame() const; Frame getCurrentFrameQuantized() const; float getCurrentSecond(int sampleRate) const; Frame getFramesInBar() const; Frame getFramesInBeat() const; Frame getFramesInLoop() const; Frame getFramesInSeq() const; int getQuantizerValue() const; int getQuantizerStep() const; SeqStatus getStatus() const; /* 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(int sampleRate) const; /* 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, int sampleRate) const; /* quantize Quantizes the frame 'f'. */ Frame quantize(Frame f) const; /* reset Brings everything back to the initial state. */ void reset(int sampleRate); /* react Reacts to live events coming from the EventDispatcher (human events). */ void react(const EventDispatcher::EventBuffer&, int sampleRate); /* advance Parses sequencer events that might occur in a block and advances the internal 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, const ActionRecorder&); /* render Renders audio coming out from the sequencer: that is, the metronome! */ void render(mcl::AudioBuffer& outBuf); void rewind(); void toggleMetronome(); void setMetronome(bool v); void setBpm(float b, int sampleRate); void setBeats(int beats, int bars, int sampleRate); void setQuantize(int q, int sampleRate); void setStatus(SeqStatus); /* recomputeFrames Updates bpm, frames, beats and so on. */ void recomputeFrames(int sampleRate); /* quantizer Used by the sequencer itself and each sample channel. */ Quantizer quantizer; std::function onAboutStart; std::function onAboutStop; std::function onBpmChange; private: /* rewindQ Rewinds sequencer, quantized mode. */ void rewindQ(Frame delta); /* raw[*] Raw functions to start, stop and rewind the sequencer. These functions must be called only when the JACK signal is received. Other modules should send a SEQUENCER_* event to the Event Dispatcher. */ void rawStart(); void rawStop(); void rawRewind(); /* rawSetBpm Raw function to set the bpm, bypassing any JACK instruction. This function must be called only by the Synchronizer when the JACK signal is received. Other modules should use the public, non-raw version setBpm(...). */ void rawSetBpm(float v, int sampleRate); model::Model& m_model; Synchronizer& m_synchronizer; JackTransport& m_jackTransport; /* m_eventBuffer Buffer of events found in each block sent to channels for event parsing. This is filled during react(). */ EventBuffer m_eventBuffer; Metronome m_metronome; /* m_quantizerStep Tells how many frames to wait to perform a quantized action. */ int m_quantizerStep; }; } // namespace giada::m #endif giada-0.22.0/src/core/synchronizer.cpp000066400000000000000000000144761425106661500176420ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/synchronizer.h" #include "core/conf.h" #include "core/kernelAudio.h" #include "core/kernelMidi.h" #include "core/model/model.h" namespace giada::m { Synchronizer::Synchronizer(const Conf::Data& c, KernelMidi& k) #ifdef WITH_AUDIO_JACK : onJackRewind(nullptr) , onJackChangeBpm(nullptr) , onJackStart(nullptr) , onJackStop(nullptr) , m_kernelMidi(k) , m_conf(c) #else : m_kernelMidi(k) , m_conf(c) #endif { reset(); } /* -------------------------------------------------------------------------- */ void Synchronizer::reset() { m_midiTCrate = static_cast((m_conf.samplerate / m_conf.midiTCfps) * G_MAX_IO_CHANS); // stereo values } /* -------------------------------------------------------------------------- */ void Synchronizer::sendMIDIsync(const model::Sequencer& sequencer) { /* Sending MIDI sync while waiting is meaningless. */ if (sequencer.status == SeqStatus::WAITING) return; const int currentFrame = sequencer.a_getCurrentFrame(); /* TODO - only Master (_M) is implemented so far. */ if (m_conf.midiSync == G_MIDI_SYNC_CLOCK_M) { if (currentFrame % (sequencer.framesInBeat / 24) == 0) m_kernelMidi.send(MIDI_CLOCK, -1, -1); return; } if (m_conf.midiSync == G_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 % m_midiTCrate != 0) // no timecode frame passed return; /* frame low nibble * frame high nibble * seconds low nibble * seconds high nibble */ if (m_midiTCframes % 2 == 0) { m_kernelMidi.send(MIDI_MTC_QUARTER, (m_midiTCframes & 0x0F) | 0x00, -1); m_kernelMidi.send(MIDI_MTC_QUARTER, (m_midiTCframes >> 4) | 0x10, -1); m_kernelMidi.send(MIDI_MTC_QUARTER, (m_midiTCseconds & 0x0F) | 0x20, -1); m_kernelMidi.send(MIDI_MTC_QUARTER, (m_midiTCseconds >> 4) | 0x30, -1); } /* minutes low nibble * minutes high nibble * hours low nibble * hours high nibble SMPTE frame rate */ else { m_kernelMidi.send(MIDI_MTC_QUARTER, (m_midiTCminutes & 0x0F) | 0x40, -1); m_kernelMidi.send(MIDI_MTC_QUARTER, (m_midiTCminutes >> 4) | 0x50, -1); m_kernelMidi.send(MIDI_MTC_QUARTER, (m_midiTChours & 0x0F) | 0x60, -1); m_kernelMidi.send(MIDI_MTC_QUARTER, (m_midiTChours >> 4) | 0x70, -1); } m_midiTCframes++; /* check if total timecode frames are greater than timecode fps: * if so, a second has passed */ if (m_midiTCframes > m_conf.midiTCfps) { m_midiTCframes = 0; m_midiTCseconds++; if (m_midiTCseconds >= 60) { m_midiTCminutes++; m_midiTCseconds = 0; if (m_midiTCminutes >= 60) { m_midiTChours++; m_midiTCminutes = 0; } } //u::log::print("%d:%d:%d:%d\n", m_midiTChours, m_midiTCminutes, m_midiTCseconds, m_midiTCframes); } } } /* -------------------------------------------------------------------------- */ void Synchronizer::sendMIDIrewind() { m_midiTCframes = 0; m_midiTCseconds = 0; m_midiTCminutes = 0; m_midiTChours = 0; /* For cueing the slave to a particular start point, Quarter Frame messages are not used. Instead, an MTC Full Frame message should be sent. The Full Frame is a SysEx message that encodes the entire SMPTE time in one message. */ if (m_conf.midiSync == G_MIDI_SYNC_MTC_M) { m_kernelMidi.send(MIDI_SYSEX, 0x7F, 0x00); // send msg on channel 0 m_kernelMidi.send(0x01, 0x01, 0x00); // hours 0 m_kernelMidi.send(0x00, 0x00, 0x00); // mins, secs, frames 0 m_kernelMidi.send(MIDI_EOX, -1, -1); // end of sysex } else if (m_conf.midiSync == G_MIDI_SYNC_CLOCK_M) m_kernelMidi.send(MIDI_POSITION_PTR, 0, 0); } /* -------------------------------------------------------------------------- */ void Synchronizer::sendMIDIstart() { if (m_conf.midiSync == G_MIDI_SYNC_CLOCK_M) { m_kernelMidi.send(MIDI_START, -1, -1); m_kernelMidi.send(MIDI_POSITION_PTR, 0, 0); } } /* -------------------------------------------------------------------------- */ void Synchronizer::sendMIDIstop() { if (m_conf.midiSync == G_MIDI_SYNC_CLOCK_M) m_kernelMidi.send(MIDI_STOP, -1, -1); } /* -------------------------------------------------------------------------- */ #ifdef WITH_AUDIO_JACK void Synchronizer::recvJackSync(const JackTransport::State& state) { assert(onJackRewind != nullptr); assert(onJackChangeBpm != nullptr); assert(onJackStart != nullptr); assert(onJackStop != nullptr); JackTransport::State jackStateCurr = state; if (jackStateCurr != m_jackStatePrev) { if (jackStateCurr.frame != m_jackStatePrev.frame && jackStateCurr.frame == 0) { G_DEBUG("JackState received - rewind to frame 0"); onJackRewind(); } // jackStateCurr.bpm == 0 if JACK doesn't send that info if (jackStateCurr.bpm != m_jackStatePrev.bpm && jackStateCurr.bpm > 1.0f) { G_DEBUG("JackState received - bpm=" << jackStateCurr.bpm); onJackChangeBpm(jackStateCurr.bpm); } if (jackStateCurr.running != m_jackStatePrev.running) { G_DEBUG("JackState received - running=" << jackStateCurr.running); jackStateCurr.running ? onJackStart() : onJackStop(); } } m_jackStatePrev = jackStateCurr; } #endif } // namespace giada::m giada-0.22.0/src/core/synchronizer.h000066400000000000000000000052431425106661500172770ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_SYNC_H #define G_SYNC_H #ifdef WITH_AUDIO_JACK #include "core/jackTransport.h" #endif #include "core/types.h" #include "core/conf.h" #include namespace giada::m::kernelAudio { struct JackState; } namespace giada::m::model { class Sequencer; } namespace giada::m { class KernelMidi; class Synchronizer final { public: Synchronizer(const Conf::Data&, KernelMidi&); /* reset Brings everything back to the initial state. */ void reset(); /* sendMIDIsync Generates MIDI sync output data. */ void sendMIDIsync(const model::Sequencer& clock); /* sendMIDIrewind Rewinds timecode to beat 0 and also send a MTC full frame to cue the slave. */ void sendMIDIrewind(); void sendMIDIstart(); void sendMIDIstop(); #ifdef WITH_AUDIO_JACK /* recvJackSync Receives a new JACK state. Called by Kernel Audio on each audio block. */ void recvJackSync(const JackTransport::State& state); /* onJack[...] Callbacks called when something happens in the JACK state. */ std::function onJackRewind; std::function onJackChangeBpm; std::function onJackStart; std::function onJackStop; #endif private: /* midiTC* MIDI timecode variables. */ int m_midiTCrate = 0; // Send MTC data every m_midiTCrate frames int m_midiTCframes = 0; int m_midiTCseconds = 0; int m_midiTCminutes = 0; int m_midiTChours = 0; #ifdef WITH_AUDIO_JACK JackTransport::State m_jackStatePrev; #endif KernelMidi& m_kernelMidi; const Conf::Data& m_conf; }; } // namespace giada::m #endif giada-0.22.0/src/core/types.h000066400000000000000000000040701425106661500157030ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_TYPES_H #define G_TYPES_H namespace giada { using ID = int; using Pixel = int; using Frame = int; enum class Thread { MAIN, MIDI, AUDIO, EVENTS }; /* Windows fix */ #ifdef _WIN32 #undef VOID #endif enum class SeqStatus { STOPPED, WAITING, RUNNING, ON_BEAT, ON_BAR, ON_FIRST_BEAT, VOID }; enum class ChannelType : int { SAMPLE = 1, MIDI, MASTER, PREVIEW }; enum class ChannelStatus : int { 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, SINGLE_BASIC_PAUSE }; enum class RecTriggerMode : int { NORMAL = 0, SIGNAL }; enum class InputRecMode : int { RIGID = 0, FREE }; enum class EventType : int { AUTO = 0, MANUAL }; /* Peak Audio peak information for two In/Out channels. */ struct Peak { float left; float right; }; } // namespace giada #endif giada-0.22.0/src/core/wave.cpp000066400000000000000000000071151425106661500160370ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "wave.h" #include "const.h" #include "utils/fs.h" #include "utils/log.h" #include "utils/string.h" #include namespace giada::m { Wave::Wave(ID id) : id(id) , m_rate(0) , m_bits(0) , m_logical(false) , m_edited(false) { } /* -------------------------------------------------------------------------- */ Wave::Wave(const Wave& other) : 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) { } /* -------------------------------------------------------------------------- */ void Wave::alloc(Frame size, int channels, int rate, int bits, const std::string& path) { 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; } std::string Wave::getPath() const { return m_path; } int Wave::getBits() const { return m_bits; } bool Wave::isLogical() const { return m_logical; } bool Wave::isEdited() const { return m_edited; } /* -------------------------------------------------------------------------- */ mcl::AudioBuffer& Wave::getBuffer() { return m_buffer; } const mcl::AudioBuffer& Wave::getBuffer() const { return m_buffer; } /* -------------------------------------------------------------------------- */ int Wave::getDuration() const { return m_buffer.countFrames() / m_rate; } /* -------------------------------------------------------------------------- */ std::string Wave::getExtension() const { return u::fs::getExt(m_path); } /* -------------------------------------------------------------------------- */ 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::setPath(const std::string& p, int wid) { if (wid == -1) m_path = p; else m_path = u::fs::stripExt(p) + "-" + std::to_string(wid) + u::fs::getExt(p); } /* -------------------------------------------------------------------------- */ void Wave::replaceData(mcl::AudioBuffer&& b) { m_buffer = std::move(b); } } // namespace giada::m giada-0.22.0/src/core/wave.h000066400000000000000000000050641425106661500155050ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_WAVE_H #define G_WAVE_H #include "core/types.h" #include "deps/mcl-audio-buffer/src/audioBuffer.hpp" #include namespace giada::m { class Wave { public: Wave(ID id); Wave(const Wave& o); Wave(Wave&& o) = default; Wave& operator=(Wave&& o) = default; std::string getBasename(bool ext = false) const; std::string getExtension() 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. */ mcl::AudioBuffer& getBuffer(); const mcl::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 setRate(int v); void setLogical(bool l); void setEdited(bool e); /* replaceData Replaces internal audio buffer with 'b' by moving it. */ void replaceData(mcl::AudioBuffer&& b); void alloc(Frame size, int channels, int rate, int bits, const std::string& path); ID id; private: mcl::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 }; } // namespace giada::m #endif giada-0.22.0/src/core/waveFx.cpp000066400000000000000000000160311425106661500163320ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "waveFx.h" #include "const.h" #include "deps/mcl-audio-buffer/src/audioBuffer.hpp" #include "utils/log.h" #include "wave.h" #include #include #include namespace giada::m::wfx { namespace { void fadeFrame_(Wave& w, int i, float 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.getBuffer().countChannels(); j++) // Find highest value in any channel abs = fabs(w.getBuffer()[i][j]); if (abs > peak) peak = abs; } return peak; } } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ constexpr int SMOOTH_SIZE = 32; void normalize(Wave& w, int a, int b) { 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.getBuffer().countChannels(); j++) w.getBuffer()[i][j] = w.getBuffer()[i][j] * (1.0f / peak); } w.setEdited(true); } /* -------------------------------------------------------------------------- */ int monoToStereo(Wave& w) { if (w.getBuffer().countChannels() >= G_MAX_IO_CHANS) return G_RES_OK; mcl::AudioBuffer newData; 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.getBuffer()[i][0]; w.replaceData(std::move(newData)); return G_RES_OK; } /* -------------------------------------------------------------------------- */ void silence(Wave& w, int a, int b) { u::log::print("[wfx::silence] silencing from %d to %d\n", a, b); 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(Wave& w, int a, int b) { 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. */ int newSize = w.getBuffer().countFrames() - (b - a); mcl::AudioBuffer newData; newData.alloc(newSize, w.getBuffer().countChannels()); u::log::print("[wfx::cut] cutting from %d to %d\n", a, b); 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.replaceData(std::move(newData)); w.setEdited(true); } /* -------------------------------------------------------------------------- */ void trim(Wave& w, Frame a, Frame b) { if (a < 0) a = 0; if (b > w.getBuffer().countFrames()) b = w.getBuffer().countFrames(); Frame newSize = b - a; mcl::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); 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.replaceData(std::move(newData)); w.setEdited(true); } /* -------------------------------------------------------------------------- */ void paste(const Wave& src, Wave& des, Frame a) { assert(src.getBuffer().countChannels() == des.getBuffer().countChannels()); mcl::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) */ 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.replaceData(std::move(newData)); des.setEdited(true); } /* -------------------------------------------------------------------------- */ 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); float m = 0.0f; float d = 1.0f / (float)(b - a); if (type == Fade::IN) for (int i = a; i <= b; i++, m += d) fadeFrame_(w, i, m); else for (int i = b; i >= a; i--, m += d) fadeFrame_(w, i, m); w.setEdited(true); } /* -------------------------------------------------------------------------- */ 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)) { u::log::print("[wfx::smooth] selection is too small, nothing to do\n"); return; } fade(w, a, a + SMOOTH_SIZE, Fade::IN); fade(w, b - SMOOTH_SIZE, b, Fade::OUT); } /* -------------------------------------------------------------------------- */ void shift(Wave& w, Frame offset) { if (offset < 0) offset = (w.getBuffer().countFrames() + w.getBuffer().countChannels()) + offset; float* begin = w.getBuffer()[0]; float* end = w.getBuffer()[0] + (w.getBuffer().countFrames() * w.getBuffer().countChannels()); std::rotate(begin, end - (offset * w.getBuffer().countChannels()), end); w.setEdited(true); } /* -------------------------------------------------------------------------- */ void reverse(Wave& w, Frame a, Frame b) { /* https://stackoverflow.com/questions/33201528/reversing-an-array-of-structures-in-c */ float* begin = w.getBuffer()[0] + (a * w.getBuffer().countChannels()); float* end = w.getBuffer()[0] + (b * w.getBuffer().countChannels()); std::reverse(begin, end); w.setEdited(true); } } // namespace giada::m::wfx giada-0.22.0/src/core/waveFx.h000066400000000000000000000041521425106661500160000ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_WAVE_FX_H #define G_WAVE_FX_H #include "core/types.h" namespace giada::m { class Wave; } namespace giada::m::wfx { /* Windows fix */ #ifdef _WIN32 #undef IN #undef OUT #endif enum class Fade { IN, OUT }; /* monoToStereo 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(Wave& w, 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 'dest', starting from frame '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(Wave& w, int a, int b, Fade type); /* smooth Smooth edges of selection. */ void smooth(Wave& w, int a, int b); /* reverse Flips Wave's data. */ void reverse(Wave& v, Frame a, Frame b); void shift(Wave& w, Frame offset); } // namespace giada::m::wfx #endif giada-0.22.0/src/core/waveManager.cpp000066400000000000000000000203551425106661500173330ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "waveManager.h" #include "const.h" #include "deps/mcl-audio-buffer/src/audioBuffer.hpp" #include "idManager.h" #include "patch.h" #include "utils/fs.h" #include "utils/log.h" #include "wave.h" #include "waveFx.h" #include #include #include #include namespace giada::m { namespace { int getBits_(const SF_INFO& header) { if (header.format & SF_FORMAT_PCM_S8) return 8; else if (header.format & SF_FORMAT_PCM_16) return 16; else if (header.format & SF_FORMAT_PCM_24) return 24; else if (header.format & SF_FORMAT_PCM_32) return 32; else if (header.format & SF_FORMAT_PCM_U8) return 8; else if (header.format & SF_FORMAT_FLOAT) return 32; else if (header.format & SF_FORMAT_DOUBLE) return 64; return 0; } /* -------------------------------------------------------------------------- */ std::string makeWavePath_(const std::string& base, const m::Wave& w, int k) { return u::fs::join(base, w.getBasename(/*ext=*/false) + "-" + std::to_string(k) + w.getExtension()); } /* -------------------------------------------------------------------------- */ bool isWavePathUnique_(const m::Wave& skip, const std::string& path, const std::vector>& waves) { for (const auto& w : waves) if (w->id != skip.id && w->getPath() == path) return false; return true; } } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ std::string makeUniqueWavePath(const std::string& base, const m::Wave& w, const std::vector>& waves) { std::string path = u::fs::join(base, w.getBasename(/*ext=*/true)); if (isWavePathUnique_(w, path, waves)) return path; // TODO - just use a timestamp. e.g. makeWavePath_(..., ..., getTimeStamp()) int k = 0; path = makeWavePath_(base, w, k); while (!isWavePathUnique_(w, path, waves)) path = makeWavePath_(base, w, k++); return path; } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ void WaveManager::reset() { m_waveId = IdManager(); } /* -------------------------------------------------------------------------- */ WaveManager::Result WaveManager::createFromFile(const std::string& path, ID id, int samplerate, int quality) { if (path == "" || u::fs::isDir(path)) { u::log::print("[waveManager::create] malformed path (was '%s')\n", path); return {G_RES_ERR_NO_DATA}; } if (path.size() > FILENAME_MAX) return {G_RES_ERR_PATH_TOO_LONG}; SF_INFO header; SNDFILE* fileIn = sf_open(path.c_str(), SFM_READ, &header); if (fileIn == nullptr) { u::log::print("[waveManager::create] unable to read %s. %s\n", path, sf_strerror(fileIn)); return {G_RES_ERR_IO}; } if (header.channels > G_MAX_IO_CHANS) { u::log::print("[waveManager::create] unsupported multi-channel sample\n"); return {G_RES_ERR_WRONG_DATA}; } m_waveId.set(id); std::unique_ptr wave = std::make_unique(m_waveId.generate(id)); wave->alloc(header.frames, header.channels, header.samplerate, getBits_(header), path); 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) { u::log::print("[waveManager::create] input rate (%d) != required rate (%d), conversion needed\n", wave->getRate(), samplerate); if (resample(*wave.get(), quality, samplerate) != G_RES_OK) return {G_RES_ERR_PROCESSING}; } u::log::print("[waveManager::create] new Wave created, %d frames\n", wave->getBuffer().countFrames()); return {G_RES_OK, std::move(wave)}; } /* -------------------------------------------------------------------------- */ std::unique_ptr WaveManager::createEmpty(int frames, int channels, int samplerate, const std::string& name) { std::unique_ptr wave = std::make_unique(m_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->getBuffer().countFrames()); return wave; } /* -------------------------------------------------------------------------- */ std::unique_ptr WaveManager::createFromWave(const Wave& src, int a, int b) { int channels = src.getBuffer().countChannels(); int frames = b - a; std::unique_ptr wave = std::make_unique(m_waveId.generate()); wave->alloc(frames, channels, src.getRate(), src.getBits(), src.getPath()); 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 WaveManager::deserializeWave(const Patch::Wave& w, int samplerate, int quality) { return createFromFile(w.path, w.id, samplerate, quality).wave; } const Patch::Wave WaveManager::serializeWave(const Wave& w) const { return {w.id, u::fs::basename(w.getPath())}; } /* -------------------------------------------------------------------------- */ int WaveManager::resample(Wave& w, int quality, int samplerate) { float ratio = samplerate / (float)w.getRate(); int newSizeFrames = static_cast(ceil(w.getBuffer().countFrames() * ratio)); mcl::AudioBuffer newData; newData.alloc(newSizeFrames, w.getBuffer().countChannels()); SRC_DATA src_data; 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.getBuffer().countChannels()); if (ret != 0) { u::log::print("[waveManager::resample] resampling error: %s\n", src_strerror(ret)); return G_RES_ERR_PROCESSING; } w.replaceData(std::move(newData)); w.setRate(samplerate); return G_RES_OK; } /* -------------------------------------------------------------------------- */ int WaveManager::save(const Wave& w, const std::string& path) { SF_INFO header; header.samplerate = w.getRate(); 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) { u::log::print("[waveManager::save] unable to open %s for exporting: %s\n", path, sf_strerror(file)); return G_RES_ERR_IO; } 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; } } // namespace giada::m giada-0.22.0/src/core/waveManager.h000066400000000000000000000057231425106661500170020ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_WAVE_MANAGER_H #define G_WAVE_MANAGER_H #include "core/idManager.h" #include "core/patch.h" #include "core/types.h" #include "core/wave.h" #include #include namespace giada::m { std::string makeUniqueWavePath(const std::string& base, const m::Wave& w, const std::vector>& waves); /* -------------------------------------------------------------------------- */ class WaveManager final { public: struct Result { int status; std::unique_ptr wave = nullptr; }; /* reset Resets internal ID generator. */ void reset(); /* create Creates a new Wave object with data read from file 'path'. Pass id = 0 to auto-generate it. The function converts the Wave sample rate if it doesn't match the desired one as specified in 'samplerate'. */ Result createFromFile(const std::string& path, ID id, int samplerate, int quality); /* createEmpty Creates a new silent Wave object. */ std::unique_ptr createEmpty(int frames, int channels, int samplerate, const std::string& name); /* createFromWave Creates a new Wave from an existing one, copying the data in range a - b. */ std::unique_ptr createFromWave(const Wave& src, int a, int b); /* (de)serializeWave Creates a new Wave given the patch raw data and vice versa. */ std::unique_ptr deserializeWave(const Patch::Wave& w, int samplerate, int quality); const Patch::Wave serializeWave(const Wave& w) const; /* 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); /* save Writes Wave data to file 'path'. Only 'wav' format is supported for now. */ int save(const Wave& w, const std::string& path); private: IdManager m_waveId; }; } // namespace giada::m #endif giada-0.22.0/src/core/weakAtomic.h000066400000000000000000000037651425106661500166350ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * 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 * . * * -------------------------------------------------------------------------- */ #ifndef G_WEAK_ATOMIC_H #define G_WEAK_ATOMIC_H #include #include namespace giada { template class WeakAtomic { public: WeakAtomic() = default; WeakAtomic(T t) : m_atomic(t) , m_value(t) { } WeakAtomic(const WeakAtomic& o) : m_atomic(o.load()) , m_value(o.m_value) { } WeakAtomic(WeakAtomic&& o) = delete; WeakAtomic& operator=(const WeakAtomic& o) { if (this == &o) return *this; store(o.load()); m_value = o.m_value; return *this; } WeakAtomic& operator=(WeakAtomic&& o) = delete; T load() const { return m_atomic.load(std::memory_order_relaxed); } void store(T t) { m_atomic.store(t, std::memory_order_relaxed); if (onChange != nullptr && t != m_value) onChange(t); m_value = t; } std::function onChange = nullptr; private: std::atomic m_atomic; T m_value; }; } // namespace giada #endifgiada-0.22.0/src/core/worker.cpp000066400000000000000000000034541425106661500164100ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * 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 * . * * -------------------------------------------------------------------------- */ #include "worker.h" #include "utils/time.h" namespace giada { Worker::Worker() : m_running(false) { } /* -------------------------------------------------------------------------- */ Worker::~Worker() { stop(); } /* -------------------------------------------------------------------------- */ void Worker::start(std::function 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 giada-0.22.0/src/core/worker.h000066400000000000000000000026601425106661500160530ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * 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 * . * * -------------------------------------------------------------------------- */ #ifndef G_WORKER_H #define G_WORKER_H #include #include #include namespace giada { class Worker { public: Worker(); ~Worker(); void start(std::function f, int sleep); void stop(); private: std::thread m_thread; std::atomic m_running; }; } // namespace giada #endifgiada-0.22.0/src/deps/000077500000000000000000000000001425106661500143705ustar00rootroot00000000000000giada-0.22.0/src/deps/geompp/000077500000000000000000000000001425106661500156575ustar00rootroot00000000000000giada-0.22.0/src/deps/json/000077500000000000000000000000001425106661500153415ustar00rootroot00000000000000giada-0.22.0/src/deps/juce/000077500000000000000000000000001425106661500153165ustar00rootroot00000000000000giada-0.22.0/src/deps/juce-config.h000066400000000000000000000010151425106661500167270ustar00rootroot00000000000000#ifndef JUCE_APPCONFIG_H #define JUCE_APPCONFIG_H #include "juce/modules/juce_audio_basics/juce_audio_basics.h" #include "juce/modules/juce_audio_processors/juce_audio_processors.h" #include "juce/modules/juce_core/juce_core.h" #include "juce/modules/juce_data_structures/juce_data_structures.h" #include "juce/modules/juce_events/juce_events.h" #include "juce/modules/juce_graphics/juce_graphics.h" #include "juce/modules/juce_gui_basics/juce_gui_basics.h" #include "juce/modules/juce_gui_extra/juce_gui_extra.h" #endif giada-0.22.0/src/deps/mcl-atomic-swapper/000077500000000000000000000000001425106661500200745ustar00rootroot00000000000000giada-0.22.0/src/deps/mcl-audio-buffer/000077500000000000000000000000001425106661500175115ustar00rootroot00000000000000giada-0.22.0/src/deps/rtaudio/000077500000000000000000000000001425106661500160375ustar00rootroot00000000000000giada-0.22.0/src/deps/vst3sdk/000077500000000000000000000000001425106661500157715ustar00rootroot00000000000000giada-0.22.0/src/ext/000077500000000000000000000000001425106661500142355ustar00rootroot00000000000000giada-0.22.0/src/ext/giada.ico000066400000000000000000000226761425106661500160130ustar00rootroot0000000000000000 %(0` KKKIIIEEE#EEEDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEyEEE>EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEmMMMEEEEEE$EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE~EEEEEEIIIEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEeBBBEEEDDD*EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEECCCAAA@@@@@@AAACCCEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEJEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFKKKNNNkkkhhhNNNJJJEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE*EEEEEEEEEqEEEEEEEEEEEEEEEEEEEEEBBBAAA<<>>DDDEEEEEEEEEEEEDDDUUUHHHEEEEEEEEEEEE~EEEEEEEEEEEEBBB[[[AAAEEEEEEEEEEEEEEEEEEEEE@@@EEEEEEEEEEEEEEEHNNNEEEEEEEEEEEEEEEAAADDDEEEEEEEEEEEEEEEEEEEEEEEEEEE@@@DDDEEEEEEEEEEEEEEE"EEEEEEEEEEEEEEE[[[EEEEEEEEEEEEEEEEEEEEEDDDEEEEEEEEEFFFEEEEEEEEEEEEEEECCCEEECEEEEEEEEEEEEFFFAAAEEEEEEEEEEEEAAALLL|||uuuEEEIIIAAAEEEEEEEEEEEEEEEEEE1EEE~EEEEEEEEEEEEKKK>>>EEEEEEEEEEEEEEExxx```LLLDDDEEEEEEEEEEEEEEEEEEEEEXEEEEEEEEEEEEEEEOOODDDEEEEEEEEEAAA|||[[[VVV===EEELLLHHHEEEEEEEEEEEEEEEEEEEEEEEEEEECCCmmmrrrEEEEEEEEEEEEPPPAAABBBVVV???DDDEEEDDDRRREEEEEEEEEEEEEEEEEEEEEEEEEEEAAAZZZEEEEEEEEEEEEmmmEEEDDDAAA???>>>???BBBCCC888sssaaaDDDEEEEEEEEEEEEEEEEEEEEEEEE@@@PPPEEEEEEEEECCCEEEEEEEEEEEEHHHYYY]]]yyymmmCCCEEEEEEEEEEEEEEEEEEEEEEEE@@@PPPEEEEEEEEECCCEEEEEEEEEEEEcccpppCCCEEEEEEEEEEEEEEEEEEEEEEEEAAAZZZEEEEEEEEEEEEkkkEEEEEEEEEEEEnnn```DDDEEEEEEEEEEEEEEEEEEEEEEEECCCmmmrrrEEEEEEEEEEEEOOOAAAEEEEEEEEEEEEQQQEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEOOODDDEEEEEEEEEAAAxxxZZZDDDEEEEEEEEE@@@MMMEEEEEEEEEEEEEEEEEE~EEEEEEEEEEEEKKK>>>EEEEEEEEEEEEEEEsss\\\EEEEEEEEEEEEEEEEEEHHHEEEEEEEEEEEEEEEXEEEBEEEEEEEEEEEEFFFAAAEEEEEEEEEEEEBBBJJJyyysssCCCCCCEEEEEEEEEEEEBBBDDDEEEEEEEEEEEEEEE1EEE"EEEEEEEEEEEEEEE\\\DDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDkkkdddEEEEEEEEEEEEEEEEEEJJJEEEEEEEEEEEEEEEAAAGGGDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDD]]]<<>>CCCEEEEEEEEEEEEEEEEEEBBB???EEEEEEEEEEEEEEEEEENNNEEEEEEEEEEEEEEECCCvvvxxxbbbXXXXXXddd|||VVVDDDEEEEEEEEEEEEEEEmEEE#EEEEEEEEEEEEEEECCCDDDEEEEEEEEEEEEEEEEEE EEEEEEEEEEEEEEEEEEEEEEEEBBBEEEEEEEEEEEEEEEEEENFFFEEEEEEEEEEEEEEEEEE@@@CCCEEEEEEEEEEEEEEEEEEDDDIIIDDD*EEEEEEEEEEEEEEEEEEEEEBBBEEEEEEEEEEEEEEEEEEFFFEEEEEEIEEEEEEEEEEEEEEEEEECCCsss___DDDEEEEEEEEEEEEEEEEEEFFF&EEEEEErEEEEEEEEEEEEEEEEEECCCKKKvvvHHHDDDEEEEEEEEEEEEEEEEEEEEEMFFF@@@EEEsEEEEEEEEEEEEEEEEEEEEEBBB@@@zzz>>>CCCEEEEEEEEEEEEEEEEEEEEEEEEPEEE@@@DDDKEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFJJJMMMccc]]]MMMJJJEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFF+EEEDDDFFF+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEDDDBBBAAAAAABBBDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFAAAEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEhBBBEEEEEE%EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEoWWWGGG@@@EEE#EEEEDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEzEEE?EEE??giada-0.22.0/src/ext/resource.h000066400000000000000000000000261425106661500162330ustar00rootroot00000000000000#define IDI_ICON1 101 giada-0.22.0/src/ext/resource.rc000066400000000000000000000000741425106661500164130ustar00rootroot00000000000000#include "resource.h" IDI_ICON1 ICON DISCARDABLE "giada.ico"giada-0.22.0/src/glue/000077500000000000000000000000001425106661500143715ustar00rootroot00000000000000giada-0.22.0/src/glue/actionEditor.cpp000066400000000000000000000260771425106661500175350ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "glue/actionEditor.h" #include "core/actions/action.h" #include "core/actions/actionRecorder.h" #include "core/actions/actions.h" #include "core/const.h" #include "core/engine.h" #include "core/model/model.h" #include "core/sequencer.h" #include "glue/events.h" #include "glue/recorder.h" #include extern giada::m::Engine g_engine; 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 || a2.frame == f) return -1; return f; } /* -------------------------------------------------------------------------- */ /* recordFirstEnvelopeAction_ First action ever? Add actions at boundaries. */ void recordFirstEnvelopeAction_(ID channelId, Frame frame, int value) { // 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 = g_engine.actionRecorder.rec(channelId, 0, e1); const m::Action a2 = g_engine.actionRecorder.rec(channelId, frame, e2); const m::Action a3 = g_engine.actionRecorder.rec(channelId, g_engine.sequencer.getFramesInLoop() - 1, e1); g_engine.actionRecorder.updateSiblings(a1.id, /*prev=*/a3.id, /*next=*/a2.id); // Circular loop (begin) g_engine.actionRecorder.updateSiblings(a2.id, /*prev=*/a1.id, /*next=*/a3.id); g_engine.actionRecorder.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. */ void recordNonFirstEnvelopeAction_(ID channelId, Frame frame, int value) { const m::Action a1 = g_engine.actionRecorder.getClosestAction(channelId, frame, m::MidiEvent::ENVELOPE); const m::Action a3 = a1.next != nullptr ? *a1.next : m::Action{}; assert(a1.isValid()); assert(a3.isValid()); frame = fixVerticalEnvActions_(frame, a1, a3); if (frame == -1) // Vertical points, nothing to do here return; // TODO - use MidiEvent(float) m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::ENVELOPE, 0, value); const m::Action a2 = g_engine.actionRecorder.rec(channelId, frame, e2); g_engine.actionRecorder.updateSiblings(a2.id, a1.id, a3.id); } /* -------------------------------------------------------------------------- */ bool isSinglePressMode_(ID channelId) { /* TODO - use m::model getChannel utils (to be added) */ return g_engine.model.get().getChannel(channelId).samplePlayer->mode == SamplePlayerMode::SINGLE_PRESS; } } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ SampleData::SampleData(const m::SamplePlayer& s) : channelMode(s.mode) , isLoopMode(s.isAnyLoopMode()) { } /* -------------------------------------------------------------------------- */ Data::Data(const m::Channel& c) : channelId(c.id) , channelName(c.name) , framesInSeq(g_engine.sequencer.getFramesInSeq()) , framesInBeat(g_engine.sequencer.getFramesInBeat()) , framesInBar(g_engine.sequencer.getFramesInBar()) , framesInLoop(g_engine.sequencer.getFramesInLoop()) , actions(g_engine.actionRecorder.getActionsOnChannel(c.id)) { if (c.type == ChannelType::SAMPLE) sample = std::make_optional(c.samplePlayer.value()); } /* -------------------------------------------------------------------------- */ Frame Data::getCurrentFrame() const { return g_engine.sequencer.getCurrentFrame(); } /* -------------------------------------------------------------------------- */ bool Data::isChannelPlaying() const { return g_engine.model.get().getChannel(channelId).isPlaying(); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ Data getData(ID channelId) { return Data(g_engine.model.get().getChannel(channelId)); } /* -------------------------------------------------------------------------- */ void recordMidiAction(ID channelId, int note, int velocity, Frame f1, Frame f2) { if (f2 == 0) f2 = f1 + G_DEFAULT_ACTION_SIZE; /* Avoid frame overflow. */ Frame overflow = f2 - (g_engine.sequencer.getFramesInLoop()); if (overflow > 0) { f2 -= overflow; f1 -= overflow; } m::MidiEvent e1 = m::MidiEvent(m::MidiEvent::NOTE_ON, note, velocity); m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::NOTE_OFF, note, velocity); g_engine.actionRecorder.rec(channelId, f1, f2, e1, e2); recorder::updateChannel(channelId, /*updateActionEditor=*/false); } /* -------------------------------------------------------------------------- */ void deleteMidiAction(ID channelId, const m::Action& a) { assert(a.isValid()); assert(a.event.getStatus() == m::MidiEvent::NOTE_ON); /* Send a note-off first in case we are deleting it in a middle of a key_on/key_off sequence. Check if 'next' exist first: could be orphaned. */ if (a.next != nullptr) { events::sendMidiToChannel(channelId, a.next->event, Thread::MAIN); g_engine.actionRecorder.deleteAction(a.id, a.next->id); } else g_engine.actionRecorder.deleteAction(a.id); recorder::updateChannel(channelId, /*updateActionEditor=*/false); } /* -------------------------------------------------------------------------- */ void updateMidiAction(ID channelId, const m::Action& a, int note, int velocity, Frame f1, Frame f2) { g_engine.actionRecorder.deleteAction(a.id, a.next->id); recordMidiAction(channelId, note, velocity, f1, f2); } /* -------------------------------------------------------------------------- */ void recordSampleAction(ID channelId, int type, Frame f1, Frame f2) { 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); g_engine.actionRecorder.rec(channelId, f1, f2, e1, e2); } else { m::MidiEvent e1 = m::MidiEvent(type, 0, 0); g_engine.actionRecorder.rec(channelId, f1, e1); } recorder::updateChannel(channelId, /*updateActionEditor=*/false); } /* -------------------------------------------------------------------------- */ void updateSampleAction(ID channelId, const m::Action& a, int type, Frame f1, Frame f2) { if (isSinglePressMode_(channelId)) g_engine.actionRecorder.deleteAction(a.id, a.next->id); else g_engine.actionRecorder.deleteAction(a.id); recordSampleAction(channelId, type, f1, f2); } /* -------------------------------------------------------------------------- */ void deleteSampleAction(ID channelId, const m::Action& a) { if (a.next != nullptr) // For ChannelMode::SINGLE_PRESS combo g_engine.actionRecorder.deleteAction(a.id, a.next->id); else g_engine.actionRecorder.deleteAction(a.id); recorder::updateChannel(channelId, /*updateActionEditor=*/false); } /* -------------------------------------------------------------------------- */ void recordEnvelopeAction(ID channelId, Frame f, int value) { 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 (!g_engine.actionRecorder.hasActions(channelId, m::MidiEvent::ENVELOPE)) recordFirstEnvelopeAction_(channelId, f, value); else recordNonFirstEnvelopeAction_(channelId, f, value); recorder::updateChannel(channelId, /*updateActionEditor=*/false); } /* -------------------------------------------------------------------------- */ void deleteEnvelopeAction(ID channelId, const m::Action& a) { /* Deleting a boundary action wipes out everything. If is volume, remember to restore _i and _d members in channel. */ /* TODO - move this to c::*/ /* TODO - FIX*/ if (g_engine.actionRecorder.isBoundaryEnvelopeAction(a)) { if (a.isVolumeEnvelope()) { // TODO reset all volume vars to 1.0 } g_engine.actionRecorder.clearActions(channelId, a.event.getStatus()); } else { assert(a.prev != nullptr); assert(a.next != nullptr); const m::Action a1 = *a.prev; const m::Action a1prev = *a1.prev; const m::Action a3 = *a.next; const m::Action a3next = *a3.next; /* Original status: a1--->a--->a3 Modified status: a1-------->a3 Order is important, here: first update siblings, then delete the action. Otherwise ActionRecorder::deleteAction() would complain of missing prevId/nextId no longer found. */ g_engine.actionRecorder.updateSiblings(a1.id, a1prev.id, a3.id); g_engine.actionRecorder.updateSiblings(a3.id, a1.id, a3next.id); g_engine.actionRecorder.deleteAction(a.id); } recorder::updateChannel(channelId, /*updateActionEditor=*/false); } /* -------------------------------------------------------------------------- */ void updateEnvelopeAction(ID channelId, const m::Action& a, Frame f, int value) { /* Update the action directly if it is a boundary one. Else, delete the previous one and record a new action. */ if (g_engine.actionRecorder.isBoundaryEnvelopeAction(a)) g_engine.actionRecorder.updateEvent(a.id, m::MidiEvent(m::MidiEvent::ENVELOPE, 0, value)); else { deleteEnvelopeAction(channelId, a); recordEnvelopeAction(channelId, f, value); } } /* -------------------------------------------------------------------------- */ std::vector getActions(ID channelId) { return g_engine.actionRecorder.getActionsOnChannel(channelId); } /* -------------------------------------------------------------------------- */ void updateVelocity(const m::Action& a, int value) { m::MidiEvent event(a.event); event.setVelocity(value); g_engine.actionRecorder.updateEvent(a.id, event); } } // namespace giada::c::actionEditor giada-0.22.0/src/glue/actionEditor.h000066400000000000000000000054601425106661500171730ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_GLUE_ACTION_EDITOR_H #define G_GLUE_ACTION_EDITOR_H #include "core/types.h" #include #include #include namespace giada::m { struct Action; class SamplePlayer; class Channel; } // namespace giada::m namespace giada::c::actionEditor { struct SampleData { SampleData(const m::SamplePlayer&); SamplePlayerMode channelMode; bool isLoopMode; }; struct Data { Data() = default; Data(const m::Channel&); Frame getCurrentFrame() const; bool isChannelPlaying() const; ID channelId; std::string channelName; Frame framesInSeq; Frame framesInBeat; Frame framesInBar; Frame framesInLoop; std::vector actions; std::optional sample; }; Data getData(ID channelId); /* MIDI actions. */ void recordMidiAction(ID channelId, int note, int velocity, Frame f1, Frame f2 = 0); void deleteMidiAction(ID channelId, const m::Action& a); void updateMidiAction(ID channelId, const m::Action& a, int note, int velocity, Frame f1, Frame f2); void updateVelocity(const m::Action& a, int value); /* Sample Actions. */ void recordSampleAction(ID channelId, int type, Frame f1, Frame f2 = 0); void deleteSampleAction(ID channelId, const m::Action& a); void updateSampleAction(ID channelId, const m::Action& a, int type, Frame f1, Frame f2 = 0); /* Envelope actions (only volume for now). */ void recordEnvelopeAction(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); } // namespace giada::c::actionEditor #endif giada-0.22.0/src/glue/channel.cpp000066400000000000000000000266451425106661500165220ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/mainWindow/keyboard/channel.h" #include "core/actions/actionRecorder.h" #include "core/channels/channelManager.h" #include "core/conf.h" #include "core/engine.h" #include "core/kernelAudio.h" #include "core/mixer.h" #include "core/mixerHandler.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/recorder.h" #include "core/wave.h" #include "core/waveManager.h" #include "glue/channel.h" #include "glue/main.h" #include "gui/dialogs/mainWindow.h" #include "gui/dialogs/sampleEditor.h" #include "gui/dialogs/warnings.h" #include "gui/dispatcher.h" #include "gui/elems/basics/dial.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/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/ui.h" #include "src/core/actions/actions.h" #include "utils/fs.h" #include "utils/gui.h" #include "utils/log.h" #include #include #include #include extern giada::v::Ui g_ui; extern giada::m::Engine g_engine; namespace giada::c::channel { namespace { void printLoadError_(int res) { if (res == G_RES_ERR_WRONG_DATA) v::gdAlert(g_ui.langMapper.get(v::LangMap::MESSAGE_CHANNEL_MULTICHANNOTSUPPORTED)); else if (res == G_RES_ERR_IO) v::gdAlert(g_ui.langMapper.get(v::LangMap::MESSAGE_CHANNEL_CANTREADSAMPLE)); else if (res == G_RES_ERR_PATH_TOO_LONG) v::gdAlert(g_ui.langMapper.get(v::LangMap::MESSAGE_CHANNEL_PATHTOOLONG)); else if (res == G_RES_ERR_NO_DATA) v::gdAlert(g_ui.langMapper.get(v::LangMap::MESSAGE_CHANNEL_NOFILESPECIFIED)); } } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ // TODO - just pass const Channel& SampleData::SampleData(const m::Channel& ch) : waveId(ch.samplePlayer->getWaveId()) , mode(ch.samplePlayer->mode) , isLoop(ch.samplePlayer->isAnyLoopMode()) , pitch(ch.samplePlayer->pitch) , m_channel(&ch) { } Frame SampleData::getTracker() const { return m_channel->shared->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::Channel& m) : m_channel(&m) { } /* 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) : viewDispatcher(g_ui.dispatcher) , id(c.id) , columnId(c.columnId) #ifdef WITH_VST , plugins(c.plugins) #endif , 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.type == ChannelType::SAMPLE) sample = std::make_optional(c); else if (c.type == ChannelType::MIDI) midi = std::make_optional(c); } ChannelStatus Data::getPlayStatus() const { return m_channel.shared->playStatus.load(); } ChannelStatus Data::getRecStatus() const { return m_channel.shared->recStatus.load(); } bool Data::getReadActions() const { return m_channel.shared->readActions.load(); } bool Data::isRecordingInput() const { return g_engine.recorder.isRecordingInput(); } bool Data::isRecordingAction() const { return g_engine.recorder.isRecordingAction(); } /* TODO - useless methods, turn them into member vars */ bool Data::getSolo() const { return m_channel.isSoloed(); } bool Data::getMute() const { return m_channel.isMuted(); } bool Data::isArmed() const { return m_channel.armed; } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ Data getData(ID channelId) { return Data(g_engine.model.get().getChannel(channelId)); } std::vector getChannels() { std::vector out; for (const m::Channel& ch : g_engine.model.get().channels) if (!ch.isInternal()) out.push_back(Data(ch)); return out; } /* -------------------------------------------------------------------------- */ int loadChannel(ID channelId, const std::string& fname) { auto progress = g_ui.mainWindow->getScopedProgress(g_ui.langMapper.get(v::LangMap::MESSAGE_CHANNEL_LOADINGSAMPLES)); m::WaveManager::Result res = g_engine.waveManager.createFromFile(fname, /*id=*/0, g_engine.kernelAudio.getSampleRate(), g_engine.conf.data.rsmpQuality); if (res.status != G_RES_OK) { printLoadError_(res.status); return res.status; } /* Save the patch and take the last browser's dir in order to re-use it the next time. */ g_engine.conf.data.samplePath = u::fs::dirname(fname); g_engine.mixerHandler.loadChannel(channelId, std::move(res.wave)); return G_RES_OK; } /* -------------------------------------------------------------------------- */ void addChannel(ID columnId, ChannelType type) { m::Channel& ch = g_engine.mixerHandler.addChannel(type, columnId, g_engine.kernelAudio.getBufferSize(), g_engine.channelManager); auto onSendMidiCb = [channelId = ch.id]() { g_ui.mainWindow->keyboard->notifyMidiOut(channelId); }; ch.midiLighter.onSend = onSendMidiCb; if (ch.midiSender) ch.midiSender->onSend = onSendMidiCb; } /* -------------------------------------------------------------------------- */ void addAndLoadChannels(ID columnId, const std::vector& fnames) { auto progress = g_ui.mainWindow->getScopedProgress(g_ui.langMapper.get(v::LangMap::MESSAGE_CHANNEL_LOADINGSAMPLES)); bool errors = false; int i = 0; for (const std::string& f : fnames) { progress.get().setProgress(++i / static_cast(fnames.size())); m::WaveManager::Result res = g_engine.waveManager.createFromFile(f, /*id=*/0, g_engine.kernelAudio.getSampleRate(), g_engine.conf.data.rsmpQuality); if (res.status == G_RES_OK) g_engine.mixerHandler.addAndLoadChannel(columnId, std::move(res.wave), g_engine.kernelAudio.getBufferSize(), g_engine.channelManager); else errors = true; } if (errors) v::gdAlert(g_ui.langMapper.get(v::LangMap::MESSAGE_CHANNEL_LOADINGSAMPLESERROR)); } /* -------------------------------------------------------------------------- */ void deleteChannel(ID channelId) { if (!v::gdConfirmWin(g_ui.langMapper.get(v::LangMap::COMMON_WARNING), g_ui.langMapper.get(v::LangMap::MESSAGE_CHANNEL_DELETE))) return; g_ui.closeAllSubwindows(); #ifdef WITH_VST const std::vector plugins = g_engine.model.get().getChannel(channelId).plugins; #endif g_engine.mixerHandler.deleteChannel(channelId); g_engine.actionRecorder.clearChannel(channelId); #ifdef WITH_VST g_engine.pluginHost.freePlugins(plugins); #endif } /* -------------------------------------------------------------------------- */ void freeChannel(ID channelId) { if (!v::gdConfirmWin(g_ui.langMapper.get(v::LangMap::COMMON_WARNING), g_ui.langMapper.get(v::LangMap::MESSAGE_CHANNEL_FREE))) return; g_ui.closeAllSubwindows(); g_engine.actionRecorder.clearChannel(channelId); g_engine.mixerHandler.freeChannel(channelId); } /* -------------------------------------------------------------------------- */ void setInputMonitor(ID channelId, bool value) { g_engine.model.get().getChannel(channelId).audioReceiver->inputMonitor = value; g_engine.model.swap(m::model::SwapType::SOFT); } /* -------------------------------------------------------------------------- */ void setOverdubProtection(ID channelId, bool value) { m::Channel& ch = g_engine.model.get().getChannel(channelId); ch.audioReceiver->overdubProtection = value; if (value == true && ch.armed) ch.armed = false; g_engine.model.swap(m::model::SwapType::SOFT); } /* -------------------------------------------------------------------------- */ void cloneChannel(ID channelId) { g_engine.actionRecorder.cloneActions(channelId, g_engine.channelManager.getNextId()); #ifdef WITH_VST g_engine.mixerHandler.cloneChannel(channelId, g_engine.patch.data.samplerate, g_engine.kernelAudio.getBufferSize(), g_engine.channelManager, g_engine.waveManager, g_engine.sequencer, g_engine.pluginManager); #else g_engine.mixerHandler.cloneChannel(channelId, g_engine.kernelAudio.getBufferSize(), g_engine.channelManager, g_engine.waveManager); #endif } /* -------------------------------------------------------------------------- */ void setSamplePlayerMode(ID channelId, SamplePlayerMode mode) { g_engine.model.get().getChannel(channelId).samplePlayer->mode = mode; g_engine.model.swap(m::model::SwapType::HARD); // TODO - SOFT should be enough, fix geChannel refresh method g_ui.refreshSubWindow(WID_ACTION_EDITOR); } /* -------------------------------------------------------------------------- */ void setHeight(ID channelId, Pixel p) { g_engine.model.get().getChannel(channelId).height = p; g_engine.model.swap(m::model::SwapType::SOFT); } /* -------------------------------------------------------------------------- */ void setName(ID channelId, const std::string& name) { g_engine.mixerHandler.renameChannel(channelId, name); } /* -------------------------------------------------------------------------- */ void setCallbacks(m::Channel& ch) { auto onSendMidiCb = [channelId = ch.id]() { g_ui.mainWindow->keyboard->notifyMidiOut(channelId); }; ch.midiLighter.onSend = onSendMidiCb; if (ch.midiSender) ch.midiSender->onSend = onSendMidiCb; } } // namespace giada::c::channel giada-0.22.0/src/glue/channel.h000066400000000000000000000077441425106661500161660ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_GLUE_CHANNEL_H #define G_GLUE_CHANNEL_H #include "core/model/model.h" #include "core/types.h" #include #include #include #include namespace giada::m { class Plugin; } namespace giada::v { class Dispatcher; } namespace giada::c::channel { struct SampleData { SampleData() = delete; SampleData(const m::Channel&); 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::Channel* m_channel; }; struct MidiData { MidiData() = delete; MidiData(const m::Channel&); bool isOutputEnabled() const; int getFilter() const; private: const m::Channel* m_channel; }; struct Data { Data(const m::Channel&); bool getMute() const; bool getSolo() const; ChannelStatus getPlayStatus() const; ChannelStatus getRecStatus() const; bool getReadActions() const; bool isArmed() const; bool isRecordingInput() const; bool isRecordingAction() const; v::Dispatcher& viewDispatcher; ID id; ID columnId; #ifdef WITH_VST std::vector plugins; #endif ChannelType type; Pixel height; std::string name; float volume; float pan; int key; bool hasActions; std::optional sample; std::optional midi; private: const m::Channel& m_channel; }; /* getChannels Returns a single viewModel object filled with data from a channel. */ Data getData(ID channelId); /* getChannels Returns a vector of viewModel objects filled with data from channels. */ std::vector getChannels(); /* addChannel Adds an empty new channel to the stack. */ void addChannel(ID columnId, ChannelType type); /* loadChannel Fills an existing channel with a wave. */ int loadChannel(ID columnId, const std::string& fname); /* addAndLoadChannels As above, with multiple audio file paths in input. */ void addAndLoadChannels(ID columnId, const std::vector& fpaths); /* deleteChannel Removes a channel from Mixer. */ void deleteChannel(ID channelId); /* freeChannel Unloads the sample from a sample channel. */ void freeChannel(ID channelId); /* cloneChannel Makes an exact copy of a channel. */ void cloneChannel(ID channelId); /* set* Sets several channel properties. */ void setInputMonitor(ID channelId, bool value); void setOverdubProtection(ID channelId, bool value); void setName(ID channelId, const std::string& name); void setHeight(ID channelId, Pixel p); void setSamplePlayerMode(ID channelId, SamplePlayerMode m); /* setCallbacks Install callbacks to a m::Channel object in order to communicate with the UI. Call this whenever you add a new channel. */ void setCallbacks(m::Channel&); } // namespace giada::c::channel #endif giada-0.22.0/src/glue/config.cpp000066400000000000000000000257351425106661500163560ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "glue/config.h" #include "core/conf.h" #include "core/const.h" #include "core/engine.h" #include "core/kernelAudio.h" #include "core/kernelMidi.h" #include "core/midiMapper.h" #include "core/plugins/pluginManager.h" #include "deps/rtaudio/RtAudio.h" #include "gui/dialogs/browser/browserDir.h" #include "gui/dialogs/config.h" #include "gui/dialogs/warnings.h" #include "gui/elems/config/tabPlugins.h" #include "gui/ui.h" #include "utils/fs.h" #include "utils/vector.h" #include #include extern giada::v::Ui g_ui; extern giada::m::Engine g_engine; namespace giada::c::config { namespace { AudioDeviceData getAudioDeviceData_(DeviceType type, size_t index, int channelsCount, int channelsStart) { for (const m::KernelAudio::Device& device : g_engine.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 (g_engine.kernelAudio.hasAPI(RtAudio::LINUX_ALSA)) audioData.apis[G_SYS_API_ALSA] = "ALSA"; if (g_engine.kernelAudio.hasAPI(RtAudio::UNIX_JACK)) audioData.apis[G_SYS_API_JACK] = "JACK"; if (g_engine.kernelAudio.hasAPI(RtAudio::LINUX_PULSE)) audioData.apis[G_SYS_API_PULSE] = "PulseAudio"; #elif defined(G_OS_FREEBSD) if (g_engine.kernelAudio.hasAPI(RtAudio::UNIX_JACK)) audioData.apis[G_SYS_API_JACK] = "JACK"; if (g_engine.kernelAudio.hasAPI(RtAudio::LINUX_PULSE)) audioData.apis[G_SYS_API_PULSE] = "PulseAudio"; #elif defined(G_OS_WINDOWS) if (g_engine.kernelAudio.hasAPI(RtAudio::WINDOWS_DS)) audioData.apis[G_SYS_API_DS] = "DirectSound"; if (g_engine.kernelAudio.hasAPI(RtAudio::WINDOWS_ASIO)) audioData.apis[G_SYS_API_ASIO] = "ASIO"; if (g_engine.kernelAudio.hasAPI(RtAudio::WINDOWS_WASAPI)) audioData.apis[G_SYS_API_WASAPI] = "WASAPI"; #elif defined(G_OS_MAC) if (g_engine.kernelAudio.hasAPI(RtAudio::MACOSX_CORE)) audioData.apis[G_SYS_API_CORE] = "CoreAudio"; #endif std::vector devices = g_engine.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 = g_engine.conf.data.soundSystem; audioData.bufferSize = g_engine.conf.data.buffersize; audioData.sampleRate = g_engine.conf.data.samplerate; audioData.limitOutput = g_engine.conf.data.limitOutput; audioData.recTriggerLevel = g_engine.conf.data.recTriggerLevel; audioData.resampleQuality = g_engine.conf.data.rsmpQuality; audioData.outputDevice = getAudioDeviceData_(DeviceType::OUTPUT, g_engine.conf.data.soundDeviceOut, g_engine.conf.data.channelsOutCount, g_engine.conf.data.channelsOutStart); audioData.inputDevice = getAudioDeviceData_(DeviceType::INPUT, g_engine.conf.data.soundDeviceIn, g_engine.conf.data.channelsInCount, g_engine.conf.data.channelsInStart); return audioData; } /* -------------------------------------------------------------------------- */ MidiData getMidiData() { MidiData midiData; #if defined(G_OS_LINUX) if (g_engine.kernelMidi.hasAPI(RtMidi::LINUX_ALSA)) midiData.apis[G_MIDI_API_ALSA] = "ALSA"; if (g_engine.kernelMidi.hasAPI(RtMidi::UNIX_JACK)) midiData.apis[G_MIDI_API_JACK] = "JACK"; #elif defined(G_OS_FREEBSD) if (g_engine.kernelMidi.hasAPI(RtMidi::UNIX_JACK)) midiData.apis[G_MIDI_API_JACK] = "JACK"; #elif defined(G_OS_WINDOWS) if (g_engine.kernelMidi.hasAPI(RtMidi::WINDOWS_MM)) midiData.apis[G_MIDI_API_MM] = "Multimedia MIDI"; #elif defined(G_OS_MAC) if (g_engine.kernelMidi.hasAPI(RtMidi::MACOSX_CORE)) midiData.apis[G_MIDI_API_CORE] = "OSX Core MIDI"; #endif midiData.syncModes[G_MIDI_SYNC_NONE] = "(disabled)"; midiData.syncModes[G_MIDI_SYNC_CLOCK_M] = "MIDI Clock (master)"; midiData.syncModes[G_MIDI_SYNC_MTC_M] = "MTC (master)"; midiData.midiMaps = g_engine.midiMapper.getMapFilesFound(); midiData.midiMap = u::vector::indexOf(midiData.midiMaps, g_engine.conf.data.midiMapPath); for (unsigned i = 0; i < g_engine.kernelMidi.countOutPorts(); i++) midiData.outPorts.push_back(g_engine.kernelMidi.getOutPortName(i)); for (unsigned i = 0; i < g_engine.kernelMidi.countInPorts(); i++) midiData.inPorts.push_back(g_engine.kernelMidi.getInPortName(i)); midiData.api = g_engine.conf.data.midiSystem; midiData.syncMode = g_engine.conf.data.midiSync; midiData.outPort = g_engine.conf.data.midiPortOut; midiData.inPort = g_engine.conf.data.midiPortIn; return midiData; } /* -------------------------------------------------------------------------- */ #ifdef WITH_VST PluginData getPluginData() { PluginData pluginData; pluginData.numAvailablePlugins = g_engine.pluginManager.countAvailablePlugins(); pluginData.pluginPath = g_engine.conf.data.pluginPath; return pluginData; } #endif /* -------------------------------------------------------------------------- */ MiscData getMiscData() { MiscData miscData; miscData.logMode = g_engine.conf.data.logMode; miscData.showTooltips = g_engine.conf.data.showTooltips; miscData.langMaps = g_ui.langMapper.getMapFilesFound(); miscData.langMap = g_engine.conf.data.langMap; return miscData; } /* -------------------------------------------------------------------------- */ void save(const AudioData& data) { g_engine.conf.data.soundSystem = data.api; g_engine.conf.data.soundDeviceOut = data.outputDevice.index; g_engine.conf.data.soundDeviceIn = data.inputDevice.index; g_engine.conf.data.channelsOutCount = data.outputDevice.channelsCount; g_engine.conf.data.channelsOutStart = data.outputDevice.channelsStart; g_engine.conf.data.channelsInCount = data.inputDevice.channelsCount; g_engine.conf.data.channelsInStart = data.inputDevice.channelsStart; g_engine.conf.data.limitOutput = data.limitOutput; g_engine.conf.data.rsmpQuality = data.resampleQuality; g_engine.conf.data.buffersize = data.bufferSize; g_engine.conf.data.recTriggerLevel = data.recTriggerLevel; g_engine.conf.data.samplerate = data.sampleRate; g_engine.updateMixerModel(); } /* -------------------------------------------------------------------------- */ #ifdef WITH_VST void save(const PluginData& data) { g_engine.conf.data.pluginPath = data.pluginPath; } #endif /* -------------------------------------------------------------------------- */ void save(const MidiData& data) { g_engine.conf.data.midiSystem = data.api; g_engine.conf.data.midiPortOut = data.outPort; g_engine.conf.data.midiPortIn = data.inPort; //g_engine.conf.data.midiMapPath = data.midiMap >= 0 && data.midiMap < (int)data.midiMaps.size() ? data.midiMaps[data.midiMap] : ""; g_engine.conf.data.midiMapPath = u::vector::atOr(data.midiMaps, data.midiMap, ""); g_engine.conf.data.midiSync = data.syncMode; } /* -------------------------------------------------------------------------- */ void save(const MiscData& data) { g_engine.conf.data.logMode = data.logMode; g_engine.conf.data.showTooltips = data.showTooltips; g_engine.conf.data.langMap = data.langMap; Fl_Tooltip::enable(g_engine.conf.data.showTooltips); // TODO - move this to UI init } /* -------------------------------------------------------------------------- */ #ifdef WITH_VST void scanPlugins(std::string dir, const std::function& progress) { g_engine.pluginManager.scanDirs(dir, progress); g_engine.pluginManager.saveList(u::fs::join(u::fs::getHomePath(), "plugins.xml")); } /* -------------------------------------------------------------------------- */ void setPluginPathCb(void* data) { v::gdBrowserDir* browser = static_cast(data); std::string& pluginPath = g_engine.conf.data.pluginPath; if (browser->getCurrentPath() == "") { v::gdAlert(g_ui.langMapper.get(v::LangMap::CONFIG_PLUGINS_INVALIDPATH)); return; } if (!pluginPath.empty() && pluginPath.back() != ';') pluginPath += ";"; pluginPath += browser->getCurrentPath(); browser->do_callback(); v::gdConfig* configWin = static_cast(g_ui.getSubwindow(*g_ui.mainWindow.get(), WID_CONFIG)); configWin->tabPlugins->rebuild(); } #endif } // namespace giada::c::config giada-0.22.0/src/glue/config.h000066400000000000000000000066241425106661500160170ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_GLUE_CONFIG_H #define G_GLUE_CONFIG_H #include "core/kernelAudio.h" #include "core/types.h" #include #include #include 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 sampleRates = {}; /* Selectable values. */ int channelsCount = 0; int channelsStart = 0; }; struct AudioData { void setOutputDevice(int index); void setInputDevice(int index); std::map apis; std::vector outputDevices; std::vector inputDevices; /* Selectable values. */ int api; AudioDeviceData outputDevice; AudioDeviceData inputDevice; int bufferSize; int sampleRate; bool limitOutput; float recTriggerLevel; int resampleQuality; }; struct MidiData { std::map apis; std::map syncModes; std::vector midiMaps; std::vector outPorts; std::vector inPorts; /* Selectable values. */ int api; int syncMode; int midiMap; int outPort; int inPort; }; #ifdef WITH_VST struct PluginData { int numAvailablePlugins; std::string pluginPath; }; #endif struct MiscData { int logMode; bool showTooltips; std::vector langMaps; /* Selectable values. */ std::string langMap; }; /* get* Return viewModel objects filled with data. */ AudioData getAudioData(); MidiData getMidiData(); MiscData getMiscData(); #ifdef WITH_VST PluginData getPluginData(); #endif void save(const AudioData&); void save(const MidiData&); void save(const MiscData&); #ifdef WITH_VST void save(const PluginData&); void scanPlugins(std::string dir, const std::function& progress); /* setPluginPathCb Callback attached to the DirBrowser for adding new Plug-in search paths in the configuration window. */ void setPluginPathCb(void* data); #endif } // namespace giada::c::config #endif giada-0.22.0/src/glue/events.cpp000066400000000000000000000216721425106661500164110ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "events.h" #include "core/conf.h" #include "core/const.h" #include "core/engine.h" #include "core/eventDispatcher.h" #include "core/kernelAudio.h" #include "core/midiEvent.h" #include "core/mixer.h" #include "core/mixerHandler.h" #include "core/model/model.h" #include "core/plugins/pluginHost.h" #include "core/recorder.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/basics/dial.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/sampleEditor/panTool.h" #include "gui/elems/sampleEditor/pitchTool.h" #include "gui/elems/sampleEditor/volumeTool.h" #include "gui/ui.h" #include "utils/log.h" #include #include extern giada::v::Ui g_ui; extern giada::m::Engine g_engine; namespace giada::c::events { namespace { void pushEvent_(m::EventDispatcher::Event e, Thread t) { bool res = true; if (t == Thread::MAIN) { res = g_engine.eventDispatcher.UIevents.push(e); } else if (t == Thread::MIDI) { res = g_engine.eventDispatcher.MidiEvents.push(e); u::gui::ScopedLock lock; g_ui.mainWindow->keyboard->notifyMidiIn(e.channelId); } else { assert(false); } if (!res) G_DEBUG("[events] Queue full!\n"); } } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ void pressChannel(ID channelId, int velocity, Thread t) { m::MidiEvent e; e.setVelocity(velocity); pushEvent_({m::EventDispatcher::EventType::KEY_PRESS, 0, channelId, velocity}, t); } void releaseChannel(ID channelId, Thread t) { pushEvent_({m::EventDispatcher::EventType::KEY_RELEASE, 0, channelId, {}}, t); } void killChannel(ID channelId, Thread 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::EventDispatcher::EventType::CHANNEL_VOLUME, 0, channelId, v}, t); sampleEditor::onRefresh(t, [v](v::gdSampleEditor& e) { e.volumeTool->update(v); }); if (t != Thread::MAIN) { u::gui::ScopedLock lock; g_ui.mainWindow->keyboard->setChannelVolume(channelId, v); } } /* -------------------------------------------------------------------------- */ void setChannelPitch(ID channelId, float v, Thread t) { v = std::clamp(v, G_MIN_PITCH, G_MAX_PITCH); pushEvent_({m::EventDispatcher::EventType::CHANNEL_PITCH, 0, channelId, v}, t); sampleEditor::onRefresh(t, [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::EventDispatcher::EventType::CHANNEL_PAN, 0, channelId, v}, Thread::MAIN); sampleEditor::onRefresh(Thread::MAIN, [v](v::gdSampleEditor& e) { e.panTool->update(v); }); } /* -------------------------------------------------------------------------- */ void toggleMuteChannel(ID channelId, Thread t) { pushEvent_({m::EventDispatcher::EventType::CHANNEL_MUTE, 0, channelId, {}}, t); } void toggleSoloChannel(ID channelId, Thread t) { pushEvent_({m::EventDispatcher::EventType::CHANNEL_SOLO, 0, channelId, {}}, t); } /* -------------------------------------------------------------------------- */ void toggleArmChannel(ID channelId, Thread t) { pushEvent_({m::EventDispatcher::EventType::CHANNEL_TOGGLE_ARM, 0, channelId, {}}, t); } void toggleReadActionsChannel(ID channelId, Thread t) { pushEvent_({m::EventDispatcher::EventType::CHANNEL_TOGGLE_READ_ACTIONS, 0, channelId, {}}, t); } void killReadActionsChannel(ID channelId, Thread t) { pushEvent_({m::EventDispatcher::EventType::CHANNEL_KILL_READ_ACTIONS, 0, channelId, {}}, t); } /* -------------------------------------------------------------------------- */ void sendMidiToChannel(ID channelId, m::MidiEvent e, Thread t) { pushEvent_({m::EventDispatcher::EventType::MIDI, 0, channelId, m::Action{0, channelId, 0, e}}, t); } /* -------------------------------------------------------------------------- */ void toggleMetronome() { g_engine.sequencer.toggleMetronome(); } /* -------------------------------------------------------------------------- */ void setMasterInVolume(float v, Thread t) { pushEvent_({m::EventDispatcher::EventType::CHANNEL_VOLUME, 0, m::Mixer::MASTER_IN_CHANNEL_ID, v}, t); if (t != Thread::MAIN) { u::gui::ScopedLock lock; g_ui.mainWindow->mainIO->setInVol(v); } } void setMasterOutVolume(float v, Thread t) { pushEvent_({m::EventDispatcher::EventType::CHANNEL_VOLUME, 0, m::Mixer::MASTER_OUT_CHANNEL_ID, v}, t); if (t != Thread::MAIN) { u::gui::ScopedLock lock; g_ui.mainWindow->mainIO->setOutVol(v); } } /* -------------------------------------------------------------------------- */ void multiplyBeats() { main::setBeats(g_engine.sequencer.getBeats() * 2, g_engine.sequencer.getBars()); } void divideBeats() { main::setBeats(g_engine.sequencer.getBeats() / 2, g_engine.sequencer.getBars()); } /* -------------------------------------------------------------------------- */ void startSequencer(Thread t) { pushEvent_({m::EventDispatcher::EventType::SEQUENCER_START, 0, 0, {}}, t); } void stopSequencer(Thread t) { pushEvent_({m::EventDispatcher::EventType::SEQUENCER_STOP, 0, 0, {}}, t); } void toggleSequencer(Thread t) { g_engine.sequencer.isRunning() ? stopSequencer(t) : startSequencer(t); } void rewindSequencer(Thread t) { pushEvent_({m::EventDispatcher::EventType::SEQUENCER_REWIND, 0, 0, {}}, t); } /* -------------------------------------------------------------------------- */ void stopActionRecording() { if (g_engine.kernelAudio.isReady() && g_engine.recorder.isRecordingAction()) g_engine.recorder.stopActionRec(g_engine.actionRecorder); } /* -------------------------------------------------------------------------- */ void toggleActionRecording() { if (!g_engine.kernelAudio.isReady()) return; if (g_engine.recorder.isRecordingAction()) g_engine.recorder.stopActionRec(g_engine.actionRecorder); else g_engine.recorder.prepareActionRec(g_engine.conf.data.recTriggerMode); } /* -------------------------------------------------------------------------- */ void stopInputRecording() { if (g_engine.kernelAudio.isReady() && g_engine.recorder.isRecordingInput()) g_engine.recorder.stopInputRec(g_engine.conf.data.inputRecMode, g_engine.kernelAudio.getSampleRate()); } /* -------------------------------------------------------------------------- */ void toggleInputRecording() { if (!g_engine.kernelAudio.isReady() || !g_engine.kernelAudio.isInputEnabled() || !g_engine.mixerHandler.hasInputRecordableChannels()) return; if (g_engine.recorder.isRecordingInput()) g_engine.recorder.stopInputRec(g_engine.conf.data.inputRecMode, g_engine.kernelAudio.getSampleRate()); else g_engine.recorder.prepareInputRec(g_engine.conf.data.recTriggerMode, g_engine.conf.data.inputRecMode); } /* -------------------------------------------------------------------------- */ #ifdef WITH_VST void setPluginParameter(ID channelId, ID pluginId, int paramIndex, float value, Thread t) { if (t == Thread::MIDI) { u::gui::ScopedLock lock; g_ui.mainWindow->keyboard->notifyMidiIn(channelId); } g_engine.pluginHost.setPluginParameter(pluginId, paramIndex, value); c::plugin::updateWindow(pluginId, t); } #endif } // namespace giada::c::events giada-0.22.0/src/glue/events.h000066400000000000000000000054261425106661500160550ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_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::m { class MidiEvent; } 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 toggleReadActionsChannel(ID channelId, 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 stopActionRecording(); void toggleActionRecording(); void stopInputRecording(); void toggleInputRecording(); /* Plug-ins. */ #ifdef WITH_VST void setPluginParameter(ID channelId, ID pluginId, int paramIndex, float value, Thread); #endif } // namespace giada::c::events #endif giada-0.22.0/src/glue/io.cpp000066400000000000000000000216201425106661500155050ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "glue/io.h" #include "core/conf.h" #include "core/engine.h" #include "core/kernelAudio.h" #include "core/midiDispatcher.h" #include "core/mixer.h" #include "core/mixerHandler.h" #include "core/model/model.h" #include "core/recorder.h" #include "core/wave.h" #include "glue/channel.h" #include "glue/main.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/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 "gui/ui.h" #include "src/core/actions/actionRecorder.h" #include "src/core/actions/actions.h" #include "utils/log.h" #include "utils/math.h" #include extern giada::v::Ui g_ui; extern giada::m::Engine g_engine; namespace giada::c::io { namespace { void rebuildMidiWindows_() { g_ui.rebuildSubWindow(WID_MIDI_INPUT); g_ui.rebuildSubWindow(WID_MIDI_OUTPUT); } /* -------------------------------------------------------------------------- */ bool isValidKey_(int key) { if (strlen(Fl::event_text()) == 0) return false; for (const int& bind : g_engine.conf.data.keyBindings) if (key == bind) return false; return true; } } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ Channel_InputData::Channel_InputData(const m::Channel& 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 (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()}); plugins.push_back(pd); } #endif } /* -------------------------------------------------------------------------- */ MidiChannel_OutputData::MidiChannel_OutputData(const m::MidiSender& s) : enabled(s.enabled) , filter(s.filter) { } /* -------------------------------------------------------------------------- */ Channel_OutputData::Channel_OutputData(const m::Channel& 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(*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) , beatDouble(midiIn.beatDouble) , beatHalf(midiIn.beatHalf) , metronome(midiIn.metronome) { } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ Channel_InputData channel_getInputData(ID channelId) { return Channel_InputData(g_engine.model.get().getChannel(channelId)); } /* -------------------------------------------------------------------------- */ Channel_OutputData channel_getOutputData(ID channelId) { return Channel_OutputData(g_engine.model.get().getChannel(channelId)); } /* -------------------------------------------------------------------------- */ Master_InputData master_getInputData() { return Master_InputData(g_engine.model.get().midiIn); } /* -------------------------------------------------------------------------- */ void channel_enableMidiLearn(ID channelId, bool v) { g_engine.model.get().getChannel(channelId).midiLearner.enabled = v; g_engine.model.swap(m::model::SwapType::NONE); rebuildMidiWindows_(); } /* -------------------------------------------------------------------------- */ void channel_enableMidiLightning(ID channelId, bool v) { g_engine.model.get().getChannel(channelId).midiLighter.enabled = v; g_engine.model.swap(m::model::SwapType::NONE); rebuildMidiWindows_(); } /* -------------------------------------------------------------------------- */ void channel_enableMidiOutput(ID channelId, bool v) { g_engine.model.get().getChannel(channelId).midiSender->enabled = v; g_engine.model.swap(m::model::SwapType::NONE); rebuildMidiWindows_(); } /* -------------------------------------------------------------------------- */ void channel_enableVelocityAsVol(ID channelId, bool v) { g_engine.model.get().getChannel(channelId).samplePlayer->velocityAsVol = v; g_engine.model.swap(m::model::SwapType::NONE); } /* -------------------------------------------------------------------------- */ void channel_setMidiInputFilter(ID channelId, int ch) { g_engine.model.get().getChannel(channelId).midiLearner.filter = ch; g_engine.model.swap(m::model::SwapType::NONE); } void channel_setMidiOutputFilter(ID channelId, int ch) { g_engine.model.get().getChannel(channelId).midiSender->filter = ch; g_engine.model.swap(m::model::SwapType::NONE); } /* -------------------------------------------------------------------------- */ bool channel_setKey(ID channelId, int k) { if (!isValidKey_(k)) return false; g_engine.model.get().getChannel(channelId).key = k; g_engine.model.swap(m::model::SwapType::HARD); return true; } /* -------------------------------------------------------------------------- */ void channel_startMidiLearn(int param, ID channelId) { g_engine.midiDispatcher.startChannelLearn(param, channelId, rebuildMidiWindows_); } void master_startMidiLearn(int param) { g_engine.midiDispatcher.startMasterLearn(param, rebuildMidiWindows_); } #ifdef WITH_VST void plugin_startMidiLearn(int paramIndex, ID pluginId) { g_engine.midiDispatcher.startPluginLearn(paramIndex, pluginId, rebuildMidiWindows_); } #endif /* -------------------------------------------------------------------------- */ void stopMidiLearn() { g_engine.midiDispatcher.stopLearn(); rebuildMidiWindows_(); } /* -------------------------------------------------------------------------- */ void channel_clearMidiLearn(int param, ID channelId) { g_engine.midiDispatcher.clearChannelLearn(param, channelId, rebuildMidiWindows_); } void master_clearMidiLearn(int param) { g_engine.midiDispatcher.clearMasterLearn(param, rebuildMidiWindows_); } #ifdef WITH_VST void plugin_clearMidiLearn(int param, ID pluginId) { g_engine.midiDispatcher.clearPluginLearn(param, pluginId, rebuildMidiWindows_); } #endif /* -------------------------------------------------------------------------- */ void master_enableMidiLearn(bool v) { g_engine.model.get().midiIn.enabled = v; g_engine.model.swap(m::model::SwapType::NONE); rebuildMidiWindows_(); } /* -------------------------------------------------------------------------- */ void master_setMidiFilter(int c) { g_engine.model.get().midiIn.filter = c; g_engine.model.swap(m::model::SwapType::NONE); } } // namespace giada::c::io giada-0.22.0/src/glue/io.h000066400000000000000000000075041425106661500151570ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_GLUE_IO_H #define G_GLUE_IO_H #include "core/midiEvent.h" #include "core/model/model.h" #include "core/types.h" namespace giada::m { class Channel; } namespace giada::c::io { struct PluginParamData { int index; std::string name; uint32_t value; }; struct PluginData { ID id; std::string name; std::vector 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 plugins; }; struct Master_InputData { Master_InputData() = default; Master_InputData(const m::model::MidiIn&); bool enabled; int filter; uint32_t rewind; uint32_t startStop; uint32_t actionRec; uint32_t inputRec; uint32_t volumeIn; uint32_t volumeOut; uint32_t beatDouble; uint32_t beatHalf; uint32_t metronome; }; struct MidiChannel_OutputData { MidiChannel_OutputData(const m::MidiSender&); bool enabled; int filter; }; struct Channel_OutputData { Channel_OutputData() = default; Channel_OutputData(const m::Channel&); ID channelId; bool lightningEnabled; uint32_t lightningPlaying; uint32_t lightningMute; uint32_t lightningSolo; std::optional output; }; Channel_InputData channel_getInputData(ID channelId); Channel_OutputData channel_getOutputData(ID channelId); Master_InputData master_getInputData(); /* Channel functions. */ void channel_enableMidiLearn(ID channelId, bool v); void channel_enableMidiLightning(ID channelId, bool v); void channel_enableMidiOutput(ID channelId, bool v); void channel_enableVelocityAsVol(ID channelId, bool v); void channel_setMidiInputFilter(ID channelId, int c); void channel_setMidiOutputFilter(ID channelId, int c); /* channel_setKey Set key 'k' to Sample Channel 'channelId'. Used for keyboard bindings. Returns false if the key is not valid (because used for global bindings). */ bool channel_setKey(ID channelId, int k); /* MIDI Learning functions. */ 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 stopMidiLearn(); #ifdef WITH_VST 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); } // namespace giada::c::io #endif giada-0.22.0/src/glue/layout.cpp000066400000000000000000000210611425106661500164120ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "glue/layout.h" #include "core/conf.h" #include "core/engine.h" #include "core/patch.h" #include "core/sequencer.h" #include "glue/channel.h" #include "glue/config.h" #include "glue/io.h" #include "glue/storage.h" #include "gui/dialogs/about.h" #include "gui/dialogs/actionEditor/midiActionEditor.h" #include "gui/dialogs/actionEditor/sampleActionEditor.h" #include "gui/dialogs/beatsInput.h" #include "gui/dialogs/bpmInput.h" #include "gui/dialogs/browser/browserDir.h" #include "gui/dialogs/browser/browserLoad.h" #include "gui/dialogs/browser/browserSave.h" #include "gui/dialogs/channelNameInput.h" #include "gui/dialogs/config.h" #include "gui/dialogs/keyGrabber.h" #include "gui/dialogs/mainWindow.h" #include "gui/dialogs/midiIO/midiInputChannel.h" #include "gui/dialogs/midiIO/midiInputMaster.h" #include "gui/dialogs/midiIO/midiOutputMidiCh.h" #include "gui/dialogs/midiIO/midiOutputSampleCh.h" #include "gui/dialogs/missingAssets.h" #include "gui/dialogs/pluginChooser.h" #include "gui/dialogs/pluginList.h" #include "gui/dialogs/sampleEditor.h" #include "gui/ui.h" extern giada::v::Ui g_ui; extern giada::m::Engine g_engine; namespace giada::c::layout { void openBrowserForProjectLoad() { v::gdWindow* childWin = new v::gdBrowserLoad(g_ui.langMapper.get(v::LangMap::BROWSER_OPENPROJECT), g_engine.conf.data.patchPath, c::storage::loadProject, 0, g_engine.conf.data); g_ui.openSubWindow(*g_ui.mainWindow.get(), childWin, WID_FILE_BROWSER); } /* -------------------------------------------------------------------------- */ void openBrowserForProjectSave() { v::gdWindow* childWin = new v::gdBrowserSave(g_ui.langMapper.get(v::LangMap::BROWSER_SAVEPROJECT), g_engine.conf.data.patchPath, g_engine.patch.data.name, c::storage::saveProject, 0, g_engine.conf.data); g_ui.openSubWindow(*g_ui.mainWindow.get(), childWin, WID_FILE_BROWSER); } /* -------------------------------------------------------------------------- */ void openBrowserForSampleLoad(ID channelId) { v::gdWindow* w = new v::gdBrowserLoad(g_ui.langMapper.get(v::LangMap::BROWSER_OPENSAMPLE), g_engine.conf.data.samplePath.c_str(), c::storage::loadSample, channelId, g_engine.conf.data); g_ui.openSubWindow(*g_ui.mainWindow.get(), w, WID_FILE_BROWSER); } /* -------------------------------------------------------------------------- */ void openBrowserForSampleSave(ID channelId) { v::gdWindow* w = new v::gdBrowserSave(g_ui.langMapper.get(v::LangMap::BROWSER_SAVESAMPLE), g_engine.conf.data.samplePath.c_str(), "", c::storage::saveSample, channelId, g_engine.conf.data); g_ui.openSubWindow(*g_ui.mainWindow.get(), w, WID_FILE_BROWSER); } /* -------------------------------------------------------------------------- */ void openAboutWindow() { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdAbout(), WID_ABOUT); } /* -------------------------------------------------------------------------- */ void openKeyGrabberWindow(int key, std::function f) { v::gdKeyGrabber* keyGrabber = new v::gdKeyGrabber(key); keyGrabber->onSetKey = f; g_ui.openSubWindow(*g_ui.mainWindow.get(), keyGrabber, WID_KEY_GRABBER); } /* -------------------------------------------------------------------------- */ void openBpmWindow(std::string bpmValue) { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdBpmInput(bpmValue.c_str()), WID_BPM); } /* -------------------------------------------------------------------------- */ void openBeatsWindow(int beats, int bars) { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdBeatsInput(beats, bars), WID_BEATS); } /* -------------------------------------------------------------------------- */ void openConfigWindow() { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdConfig(400, 370, g_engine.conf.data), WID_CONFIG); } /* -------------------------------------------------------------------------- */ void openMasterMidiInputWindow() { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdMidiInputMaster(g_engine.conf.data), WID_MIDI_INPUT); } /* -------------------------------------------------------------------------- */ void openChannelMidiInputWindow(ID channelId) { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdMidiInputChannel(channelId, g_engine.conf.data), WID_MIDI_INPUT); } /* -------------------------------------------------------------------------- */ void openSampleChannelMidiOutputWindow(ID channelId) { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdMidiOutputSampleCh(channelId), WID_MIDI_OUTPUT); } /* -------------------------------------------------------------------------- */ void openMidiChannelMidiOutputWindow(ID channelId) { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdMidiOutputMidiCh(channelId), WID_MIDI_OUTPUT); } /* -------------------------------------------------------------------------- */ void openSampleActionEditor(ID channelId) { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdSampleActionEditor(channelId, g_engine.conf.data), WID_ACTION_EDITOR); } /* -------------------------------------------------------------------------- */ void openMidiActionEditor(ID channelId) { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdMidiActionEditor(channelId, g_engine.conf.data), WID_ACTION_EDITOR); } /* -------------------------------------------------------------------------- */ void openSampleEditor(ID channelId) { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdSampleEditor(channelId, g_engine.conf.data), WID_SAMPLE_EDITOR); } /* -------------------------------------------------------------------------- */ void openRenameChannelWindow(const c::channel::Data& data) { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdChannelNameInput(data), WID_SAMPLE_NAME); } /* -------------------------------------------------------------------------- */ void openMissingAssetsWindow(const m::LoadState& state) { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdMissingAssets(state), WID_MISSING_ASSETS); } /* -------------------------------------------------------------------------- */ #ifdef WITH_VST void openBrowserForPlugins(v::gdWindow& parent) { v::gdBrowserDir* browser = new v::gdBrowserDir(g_ui.langMapper.get(v::LangMap::BROWSER_OPENPLUGINSDIR), g_engine.conf.data.patchPath, c::config::setPluginPathCb, g_engine.conf.data); parent.addSubWindow(browser); } /* -------------------------------------------------------------------------- */ void openChannelPluginListWindow(ID channelId) { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdPluginList(channelId, g_engine.conf.data), WID_FX_LIST); } /* -------------------------------------------------------------------------- */ void openMasterInPluginListWindow() { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdPluginList(m::Mixer::MASTER_IN_CHANNEL_ID, g_engine.conf.data), WID_FX_LIST); } /* -------------------------------------------------------------------------- */ void openMasterOutPluginListWindow() { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdPluginList(m::Mixer::MASTER_OUT_CHANNEL_ID, g_engine.conf.data), WID_FX_LIST); } /* -------------------------------------------------------------------------- */ void openPluginChooser(ID channelId) { g_ui.openSubWindow(*g_ui.mainWindow.get(), new v::gdPluginChooser(g_engine.conf.data.pluginChooserX, g_engine.conf.data.pluginChooserY, g_engine.conf.data.pluginChooserW, g_engine.conf.data.pluginChooserH, channelId, g_engine.conf.data), WID_FX_CHOOSER); } #endif } // namespace giada::c::layout giada-0.22.0/src/glue/layout.h000066400000000000000000000046251425106661500160660ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_GLUE_LAYOUT_H #define G_GLUE_LAYOUT_H #include "core/types.h" #include #include namespace giada::m { struct LoadState; } namespace giada::v { class gdWindow; } namespace giada::c::channel { struct Data; } namespace giada::c::layout { void openBrowserForProjectLoad(); void openBrowserForProjectSave(); void openBrowserForSampleLoad(ID channelId); void openBrowserForSampleSave(ID channelId); void openAboutWindow(); void openKeyGrabberWindow(int key, std::function); void openBpmWindow(std::string bpmValue); void openBeatsWindow(int beats, int bars); void openConfigWindow(); void openMasterMidiInputWindow(); void openChannelMidiInputWindow(ID channelId); void openSampleChannelMidiOutputWindow(ID channelId); void openMidiChannelMidiOutputWindow(ID channelId); void openSampleActionEditor(ID channelId); void openMidiActionEditor(ID channelId); void openSampleEditor(ID channelId); void openRenameChannelWindow(const c::channel::Data&); void openMissingAssetsWindow(const m::LoadState&); #ifdef WITH_VST void openBrowserForPlugins(v::gdWindow& parent); void openChannelPluginListWindow(ID channelId); void openMasterInPluginListWindow(); void openMasterOutPluginListWindow(); void openPluginChooser(ID channelId); #endif } // namespace giada::c::layout #endif giada-0.22.0/src/glue/main.cpp000066400000000000000000000216061425106661500160260ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "glue/main.h" #include "core/conf.h" #include "core/const.h" #include "core/engine.h" #include "core/init.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/recorder.h" #include "core/sequencer.h" #include "core/synchronizer.h" #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 "gui/ui.h" #include "src/core/actions/actionRecorder.h" #include "src/core/actions/actions.h" #include "utils/gui.h" #include "utils/log.h" #include "utils/string.h" #include #include #include extern giada::v::Ui g_ui; extern giada::m::Engine g_engine; namespace giada::c::main { Timer::Timer(const m::model::Sequencer& c) : bpm(c.bpm) , beats(c.beats) , bars(c.bars) , quantize(c.quantize) , isUsingJack(g_engine.kernelAudio.getAPI() == G_SYS_API_JACK) , isRecordingInput(g_engine.recorder.isRecordingInput()) { } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ IO::IO(const m::Channel& out, const m::Channel& in, const m::model::Mixer& m) : masterOutVol(out.volume) , masterInVol(in.volume) #ifdef WITH_VST , masterOutHasPlugins(out.plugins.size() > 0) , masterInHasPlugins(in.plugins.size() > 0) #endif , inToOut(m.inToOut) { } /* -------------------------------------------------------------------------- */ Peak IO::getMasterOutPeak() { return g_engine.mixer.getPeakOut(); } Peak IO::getMasterInPeak() { return g_engine.mixer.getPeakIn(); } /* -------------------------------------------------------------------------- */ bool IO::isKernelReady() { return g_engine.kernelAudio.isReady(); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ Timer getTimer() { return Timer(g_engine.model.get().sequencer); } /* -------------------------------------------------------------------------- */ IO getIO() { return IO(g_engine.model.get().getChannel(m::Mixer::MASTER_OUT_CHANNEL_ID), g_engine.model.get().getChannel(m::Mixer::MASTER_IN_CHANNEL_ID), g_engine.model.get().mixer); } /* -------------------------------------------------------------------------- */ Sequencer getSequencer() { Sequencer out; m::Mixer::RecordInfo recInfo = g_engine.mixer.getRecordInfo(); out.isFreeModeInputRec = g_engine.recorder.isRecordingInput() && g_engine.conf.data.inputRecMode == InputRecMode::FREE; out.shouldBlink = g_ui.shouldBlink() && (g_engine.sequencer.getStatus() == SeqStatus::WAITING || out.isFreeModeInputRec); out.beats = g_engine.sequencer.getBeats(); out.bars = g_engine.sequencer.getBars(); out.currentBeat = g_engine.sequencer.getCurrentBeat(); out.recPosition = recInfo.position; out.recMaxLength = recInfo.maxLength; return out; } /* -------------------------------------------------------------------------- */ Transport getTransport() { Transport transport; transport.isRunning = g_engine.sequencer.isRunning(); transport.isRecordingAction = g_engine.recorder.isRecordingAction(); transport.isRecordingInput = g_engine.recorder.isRecordingInput(); transport.isMetronomeOn = g_engine.sequencer.isMetronomeOn(); transport.recTriggerMode = g_engine.conf.data.recTriggerMode; transport.inputRecMode = g_engine.conf.data.inputRecMode; return transport; } /* -------------------------------------------------------------------------- */ MainMenu getMainMenu() { MainMenu mainMenu; mainMenu.hasAudioData = g_engine.mixerHandler.hasAudioData(); mainMenu.hasActions = g_engine.mixerHandler.hasActions(); return mainMenu; } /* -------------------------------------------------------------------------- */ void setBpm(const char* i, const char* f) { /* Never change this stuff while recording audio. */ if (g_engine.recorder.isRecordingInput()) return; g_engine.sequencer.setBpm(std::atof(i) + (std::atof(f) / 10.0f), g_engine.kernelAudio.getSampleRate()); } /* -------------------------------------------------------------------------- */ void setBpm(float f) { /* Never change this stuff while recording audio. */ if (g_engine.recorder.isRecordingInput()) return; g_engine.sequencer.setBpm(f, g_engine.kernelAudio.getSampleRate()); } /* -------------------------------------------------------------------------- */ void setBeats(int beats, int bars) { /* Never change this stuff while recording audio. */ if (g_engine.recorder.isRecordingInput()) return; g_engine.sequencer.setBeats(beats, bars, g_engine.kernelAudio.getSampleRate()); g_engine.mixer.allocRecBuffer(g_engine.sequencer.getMaxFramesInLoop(g_engine.kernelAudio.getSampleRate())); } /* -------------------------------------------------------------------------- */ void quantize(int val) { g_engine.sequencer.setQuantize(val, g_engine.kernelAudio.getSampleRate()); } /* -------------------------------------------------------------------------- */ void clearAllSamples() { if (!v::gdConfirmWin(g_ui.langMapper.get(v::LangMap::COMMON_WARNING), g_ui.langMapper.get(v::LangMap::MESSAGE_MAIN_FREEALLSAMPLES))) return; g_ui.closeSubWindow(WID_SAMPLE_EDITOR); g_engine.sequencer.setStatus(SeqStatus::STOPPED); g_engine.synchronizer.sendMIDIstop(); g_engine.mixerHandler.freeAllChannels(); g_engine.actionRecorder.clearAllActions(); } /* -------------------------------------------------------------------------- */ void clearAllActions() { if (!v::gdConfirmWin(g_ui.langMapper.get(v::LangMap::COMMON_WARNING), g_ui.langMapper.get(v::LangMap::MESSAGE_MAIN_CLEARALLACTIONS))) return; g_ui.closeSubWindow(WID_ACTION_EDITOR); g_engine.actionRecorder.clearAllActions(); } /* -------------------------------------------------------------------------- */ void setInToOut(bool v) { g_engine.mixerHandler.setInToOut(v); } /* -------------------------------------------------------------------------- */ void toggleRecOnSignal() { if (!g_engine.recorder.canEnableRecOnSignal()) g_engine.conf.data.recTriggerMode = RecTriggerMode::NORMAL; else g_engine.conf.data.recTriggerMode = g_engine.conf.data.recTriggerMode == RecTriggerMode::NORMAL ? RecTriggerMode::SIGNAL : RecTriggerMode::NORMAL; g_engine.updateMixerModel(); } /* -------------------------------------------------------------------------- */ void toggleFreeInputRec() { if (!g_engine.recorder.canEnableFreeInputRec()) g_engine.conf.data.inputRecMode = InputRecMode::RIGID; else g_engine.conf.data.inputRecMode = g_engine.conf.data.inputRecMode == InputRecMode::FREE ? InputRecMode::RIGID : InputRecMode::FREE; g_engine.updateMixerModel(); } /* -------------------------------------------------------------------------- */ #ifdef G_DEBUG_MODE void printDebugInfo() { g_engine.model.debug(); } #endif /* -------------------------------------------------------------------------- */ void closeProject() { if (!v::gdConfirmWin(g_ui.langMapper.get(v::LangMap::COMMON_WARNING), g_ui.langMapper.get(v::LangMap::MESSAGE_MAIN_CLOSEPROJECT))) return; g_engine.mixer.disable(); g_ui.reset(); g_engine.reset(); g_engine.mixer.enable(); } /* -------------------------------------------------------------------------- */ void quitGiada() { m::init::closeMainWindow(); } } // namespace giada::c::main giada-0.22.0/src/glue/main.h000066400000000000000000000061061425106661500154710ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_MAIN_H #define G_MAIN_H #include "core/types.h" namespace giada::m { class Channel; } namespace giada::m::model { class Sequencer; class Mixer; } // namespace giada::m::model namespace giada::c::main { struct Timer { Timer() = default; Timer(const m::model::Sequencer& 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); float masterOutVol; float masterInVol; #ifdef WITH_VST bool masterOutHasPlugins; bool masterInHasPlugins; #endif bool inToOut; Peak getMasterOutPeak(); Peak getMasterInPeak(); bool isKernelReady(); }; struct Sequencer { bool isFreeModeInputRec; bool shouldBlink; int beats; int bars; int currentBeat; Frame recPosition; Frame recMaxLength; }; struct Transport { bool isRunning; bool isRecordingAction; bool isRecordingInput; bool isMetronomeOn; RecTriggerMode recTriggerMode; InputRecMode inputRecMode; }; struct MainMenu { bool hasAudioData; bool hasActions; }; /* get* Returns viewModel objects filled with data. */ Timer getTimer(); IO getIO(); Sequencer getSequencer(); Transport getTransport(); MainMenu getMainMenu(); /* setBpm (1) Sets bpm value from string to float. */ void setBpm(const char* v1, const char* v2); /* setBpm (2) Sets bpm value. Usually called from the Jack callback or non-UI components. */ void setBpm(float v); void setBeats(int beats, int bars); void quantize(int val); void clearAllSamples(); void clearAllActions(); /* setInToOut Enables the "hear what you playing" feature. */ void setInToOut(bool v); void toggleRecOnSignal(); void toggleFreeInputRec(); #ifdef G_DEBUG_MODE void printDebugInfo(); #endif /* closeProject Resets Giada to init state. If resetGui also refresh all widgets. */ void closeProject(); void quitGiada(); } // namespace giada::c::main #endif giada-0.22.0/src/glue/plugin.cpp000066400000000000000000000161211425106661500163740ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #include "core/plugins/plugin.h" #include "core/conf.h" #include "core/const.h" #include "core/engine.h" #include "core/kernelAudio.h" #include "core/mixer.h" #include "core/model/model.h" #include "core/plugins/pluginHost.h" #include "core/plugins/pluginManager.h" #include "gui/dialogs/config.h" #include "gui/dialogs/mainWindow.h" #include "gui/dialogs/pluginList.h" #include "gui/dialogs/pluginWindow.h" #include "gui/ui.h" #include "plugin.h" #include "utils/gui.h" #include #include #include extern giada::v::Ui g_ui; extern giada::m::Engine g_engine; namespace giada::c::plugin { Param::Param(const m::Plugin& p, int index, ID channelId) : index(index) , pluginId(p.id) , 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()) , currentProgram(p.getCurrentProgram()) , m_plugin(p) { for (int i = 0; i < p.getNumPrograms(); 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 { return m_plugin.createEditor(); } /* -------------------------------------------------------------------------- */ const m::Plugin& Plugin::getPluginRef() const { return m_plugin; } /* -------------------------------------------------------------------------- */ void Plugin::setResizeCallback(std::function f) { m_plugin.onEditorResize = f; } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ Plugins::Plugins(const m::Channel& c) : channelId(c.id) , plugins(c.plugins) { } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ Plugins getPlugins(ID channelId) { return Plugins(g_engine.model.get().getChannel(channelId)); } Plugin getPlugin(m::Plugin& plugin, ID channelId) { return Plugin(plugin, channelId); } Param getParam(int index, const m::Plugin& plugin, ID channelId) { return Param(plugin, index, channelId); } std::vector getPluginsInfo() { return g_engine.pluginManager.getPluginsInfo(); } /* -------------------------------------------------------------------------- */ void updateWindow(ID pluginId, Thread t) { m::Plugin* p = g_engine.model.findShared(pluginId); assert(p != nullptr); if (p->hasEditor()) return; /* Get the parent window first: the plug-in list. Then, if it exists, get the child window - the actual pluginWindow. */ v::gdPluginList* parent = static_cast(g_ui.getSubwindow(*g_ui.mainWindow.get(), WID_FX_LIST)); if (parent == nullptr) return; v::gdPluginWindow* child = static_cast(g_ui.getSubwindow(*parent, pluginId + 1)); if (child == nullptr) return; if (t != Thread::MAIN) u::gui::ScopedLock lock; child->updateParameters(t != Thread::MAIN); } /* -------------------------------------------------------------------------- */ void addPlugin(int pluginListIndex, ID channelId) { if (pluginListIndex >= g_engine.pluginManager.countAvailablePlugins()) return; std::unique_ptr plugin = g_engine.pluginManager.makePlugin(pluginListIndex, g_engine.kernelAudio.getSampleRate(), g_engine.kernelAudio.getBufferSize(), g_engine.sequencer); const m::Plugin* pluginPtr = plugin.get(); if (plugin != nullptr) g_engine.pluginHost.addPlugin(std::move(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? */ g_engine.model.get().getChannel(channelId).plugins.push_back(const_cast(pluginPtr)); g_engine.model.swap(m::model::SwapType::HARD); } /* -------------------------------------------------------------------------- */ void swapPlugins(const m::Plugin& p1, const m::Plugin& p2, ID channelId) { g_engine.pluginHost.swapPlugin(p1, p2, g_engine.model.get().getChannel(channelId).plugins); g_engine.model.swap(m::model::SwapType::HARD); } /* -------------------------------------------------------------------------- */ void sortPlugins(m::PluginManager::SortMethod method) { g_engine.pluginManager.sortPlugins(method); } /* -------------------------------------------------------------------------- */ void freePlugin(const m::Plugin& plugin, ID channelId) { u::vector::remove(g_engine.model.get().getChannel(channelId).plugins, &plugin); g_engine.model.swap(m::model::SwapType::HARD); g_engine.pluginHost.freePlugin(plugin); } /* -------------------------------------------------------------------------- */ void setProgram(ID pluginId, int programIndex) { g_engine.pluginHost.setPluginProgram(pluginId, programIndex); updateWindow(pluginId, Thread::MAIN); } /* -------------------------------------------------------------------------- */ void toggleBypass(ID pluginId) { g_engine.pluginHost.toggleBypass(pluginId); } /* -------------------------------------------------------------------------- */ void startDispatchLoop() { g_ui.startJuceDispatchLoop(); } void stopDispatchLoop() { g_ui.stopJuceDispatchLoop(); } } // namespace giada::c::plugin #endif giada-0.22.0/src/glue/plugin.h000066400000000000000000000062561425106661500160510ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_GLUE_PLUGIN_H #define G_GLUE_PLUGIN_H #ifdef WITH_VST #include "core/plugins/pluginHost.h" #include "core/plugins/pluginManager.h" #include "core/types.h" #include #include namespace juce { class AudioProcessorEditor; } namespace giada::m { class Plugin; class Channel; } // namespace giada::m namespace giada::c::plugin { struct Program { int index; std::string name; }; struct Param { 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; const m::Plugin& getPluginRef() const; void setResizeCallback(std::function f); ID id; ID channelId; bool valid; bool hasEditor; bool isBypassed; std::string name; std::string uniqueId; int currentProgram; std::vector programs; std::vector paramIndexes; private: m::Plugin& m_plugin; }; struct Plugins { Plugins() = default; Plugins(const m::Channel&); ID channelId; std::vector plugins; }; /* get* Returns ViewModel objects. */ Plugins getPlugins(ID channelId); Plugin getPlugin(m::Plugin& plugin, ID channelId); Param getParam(int index, const m::Plugin& plugin, ID channelId); std::vector getPluginsInfo(); /* updateWindow Updates the editor-less plug-in window. This is useless if the plug-in has an editor. */ void updateWindow(ID pluginId, Thread); void addPlugin(int pluginListIndex, ID channelId); void swapPlugins(const m::Plugin& p1, const m::Plugin& p2, ID channelId); void sortPlugins(m::PluginManager::SortMethod); void freePlugin(const m::Plugin& plugin, ID channelId); void setProgram(ID pluginId, int programIndex); void toggleBypass(ID pluginId); void startDispatchLoop(); void stopDispatchLoop(); } // namespace giada::c::plugin #endif #endif giada-0.22.0/src/glue/recorder.cpp000066400000000000000000000067431425106661500167140ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "glue/recorder.h" #include "core/actions/actionRecorder.h" #include "core/channels/channel.h" #include "core/const.h" #include "core/engine.h" #include "core/kernelMidi.h" #include "core/mixer.h" #include "core/model/model.h" #include "gui/dialogs/warnings.h" #include "gui/elems/mainWindow/keyboard/channel.h" #include "gui/elems/mainWindow/keyboard/sampleChannel.h" #include "gui/ui.h" #include "src/core/actions/action.h" #include "src/core/actions/actionRecorder.h" #include "src/core/actions/actions.h" #include "utils/gui.h" #include "utils/log.h" #include extern giada::m::Engine g_engine; extern giada::v::Ui g_ui; namespace giada::c::recorder { void clearAllActions(ID channelId) { if (!v::gdConfirmWin(g_ui.langMapper.get(v::LangMap::COMMON_WARNING), g_ui.langMapper.get(v::LangMap::MESSAGE_MAIN_CLEARALLACTIONS))) return; g_engine.actionRecorder.clearChannel(channelId); updateChannel(channelId, /*updateActionEditor=*/true); } /* -------------------------------------------------------------------------- */ void clearVolumeActions(ID channelId) { if (!v::gdConfirmWin(g_ui.langMapper.get(v::LangMap::COMMON_WARNING), g_ui.langMapper.get(v::LangMap::MESSAGE_MAIN_CLEARALLVOLUMEACTIONS))) return; g_engine.actionRecorder.clearActions(channelId, m::MidiEvent::ENVELOPE); updateChannel(channelId, /*updateActionEditor=*/true); } /* -------------------------------------------------------------------------- */ void clearStartStopActions(ID channelId) { if (!v::gdConfirmWin(g_ui.langMapper.get(v::LangMap::COMMON_WARNING), g_ui.langMapper.get(v::LangMap::MESSAGE_MAIN_CLEARALLSTARTSTOPACTIONS))) return; g_engine.actionRecorder.clearActions(channelId, m::MidiEvent::NOTE_ON); g_engine.actionRecorder.clearActions(channelId, m::MidiEvent::NOTE_OFF); g_engine.actionRecorder.clearActions(channelId, m::MidiEvent::NOTE_KILL); updateChannel(channelId, /*updateActionEditor=*/true); } /* -------------------------------------------------------------------------- */ void updateChannel(ID channelId, bool updateActionEditor) { /* TODO - move somewhere else in the core area */ g_engine.model.get().getChannel(channelId).hasActions = g_engine.actionRecorder.hasActions(channelId); g_engine.model.swap(m::model::SwapType::HARD); if (updateActionEditor) g_ui.refreshSubWindow(WID_ACTION_EDITOR); } } // namespace giada::c::recorder giada-0.22.0/src/glue/recorder.h000066400000000000000000000026741425106661500163600ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_GLUE_RECORDER_H #define G_GLUE_RECORDER_H #include "core/types.h" namespace giada::c::recorder { void clearAllActions(ID channelId); void clearVolumeActions(ID channelId); void clearStartStopActions(ID channelId); void updateChannel(ID channelId, bool updateActionEditor); } // namespace giada::c::recorder #endif giada-0.22.0/src/glue/sampleEditor.cpp000066400000000000000000000271231425106661500175320ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/sampleEditor.h" #include "channel.h" #include "core/const.h" #include "core/engine.h" #include "core/kernelAudio.h" #include "core/mixerHandler.h" #include "core/model/model.h" #include "core/sequencer.h" #include "core/wave.h" #include "core/waveManager.h" #include "glue/events.h" #include "gui/dialogs/mainWindow.h" #include "gui/dialogs/warnings.h" #include "gui/elems/basics/button.h" #include "gui/elems/mainWindow/keyboard/channel.h" #include "gui/elems/mainWindow/keyboard/keyboard.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/ui.h" #include "sampleEditor.h" #include "utils/gui.h" #include "utils/log.h" #include #include #include extern giada::v::Ui g_ui; extern giada::m::Engine g_engine; namespace giada::c::sampleEditor { namespace { m::Channel& getChannel_(ID channelId) { return g_engine.model.get().getChannel(channelId); } m::SamplePlayer& getSamplePlayer_(ID channelId) { return getChannel_(channelId).samplePlayer.value(); } m::Wave& getWave_(ID channelId) { return *const_cast(getSamplePlayer_(channelId).getWave()); } /* -------------------------------------------------------------------------- */ /* waveBuffer A Wave used during cut/copy/paste operations. */ std::unique_ptr waveBuffer_; Frame previewTracker_ = 0; /* -------------------------------------------------------------------------- */ /* resetBeginEnd_ Resets begin/end points to 0/max. */ void resetBeginEnd_(ID channelId) { Frame begin = getSamplePlayer_(channelId).begin; Frame end = getSamplePlayer_(channelId).getWaveSize(); setBeginEnd(channelId, begin, end); } } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ Data::Data(const m::Channel& 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) { } ChannelStatus Data::a_getPreviewStatus() const { return getChannel_(m::Mixer::PREVIEW_CHANNEL_ID).shared->playStatus.load(); } Frame Data::a_getPreviewTracker() const { return getChannel_(m::Mixer::PREVIEW_CHANNEL_ID).shared->tracker.load(); } const m::Wave& Data::getWaveRef() const { return *m_channel->samplePlayer->getWave(); } Frame Data::getFramesInBar() const { return g_engine.sequencer.getFramesInBar(); } Frame Data::getFramesInLoop() const { return g_engine.sequencer.getFramesInLoop(); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ Data getData(ID channelId) { /* Prepare the preview channel first, then return Data object. */ m::Channel& previewChannel = getChannel_(m::Mixer::PREVIEW_CHANNEL_ID); previewChannel.samplePlayer->loadWave(*previewChannel.shared, &getWave_(channelId)); g_engine.model.swap(m::model::SwapType::SOFT); return Data(getChannel_(channelId)); } /* -------------------------------------------------------------------------- */ void onRefresh(Thread t, std::function f) { v::gdSampleEditor* se = static_cast(g_ui.getSubwindow(*g_ui.mainWindow.get(), WID_SAMPLE_EDITOR)); if (se == nullptr) return; if (t != Thread::MAIN) u::gui::ScopedLock lock; f(*se); } v::gdSampleEditor* getSampleEditorWindow() { v::gdSampleEditor* se = static_cast(g_ui.getSubwindow(*g_ui.mainWindow.get(), WID_SAMPLE_EDITOR)); assert(se != nullptr); return se; } /* -------------------------------------------------------------------------- */ void setBeginEnd(ID channelId, Frame b, Frame e) { m::Channel& c = getChannel_(channelId); 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.shared->tracker.load() < b) c.shared->tracker.store(b); getSamplePlayer_(channelId).begin = b; getSamplePlayer_(channelId).end = e; g_engine.model.swap(m::model::SwapType::SOFT); /* TODO waveform widget is dumb and wants a rebuild. Refactoring needed! */ getSampleEditorWindow()->rebuild(); } /* -------------------------------------------------------------------------- */ void cut(ID channelId, Frame a, Frame b) { copy(channelId, a, b); m::model::DataLock lock = g_engine.model.lockData(); m::wfx::cut(getWave_(channelId), a, b); resetBeginEnd_(channelId); } /* -------------------------------------------------------------------------- */ void copy(ID channelId, Frame a, Frame b) { waveBuffer_ = g_engine.waveManager.createFromWave(getWave_(channelId), a, b); } /* -------------------------------------------------------------------------- */ void paste(ID channelId, Frame a) { if (!isWaveBufferFull()) { u::log::print("[sampleEditor::paste] Buffer is empty, nothing to paste\n"); return; } /* Get the existing wave in channel. */ m::Wave& wave = getWave_(channelId); /* 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::DataLock lock = g_engine.model.lockData(); /* Paste copied data to destination wave. */ m::wfx::paste(*waveBuffer_, wave, a); /* Pass the old wave that contains the pasted data to channel. */ getChannel_(channelId).samplePlayer->setWave(&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) setBeginEnd(channelId, begin, end + delta); getSampleEditorWindow()->rebuild(); } /* -------------------------------------------------------------------------- */ void silence(ID channelId, int a, int b) { m::model::DataLock lock = g_engine.model.lockData(); m::wfx::silence(getWave_(channelId), a, b); } /* -------------------------------------------------------------------------- */ void fade(ID channelId, int a, int b, m::wfx::Fade type) { m::model::DataLock lock = g_engine.model.lockData(); m::wfx::fade(getWave_(channelId), a, b, type); } /* -------------------------------------------------------------------------- */ void smoothEdges(ID channelId, int a, int b) { m::model::DataLock lock = g_engine.model.lockData(); m::wfx::smooth(getWave_(channelId), a, b); } /* -------------------------------------------------------------------------- */ void reverse(ID channelId, Frame a, Frame b) { m::model::DataLock lock = g_engine.model.lockData(); m::wfx::reverse(getWave_(channelId), a, b); } /* -------------------------------------------------------------------------- */ void normalize(ID channelId, int a, int b) { m::model::DataLock lock = g_engine.model.lockData(); m::wfx::normalize(getWave_(channelId), a, b); } /* -------------------------------------------------------------------------- */ void trim(ID channelId, int a, int b) { m::model::DataLock lock = g_engine.model.lockData(); 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 channel. */ setPreviewTracker(previewTracker_); getSampleEditorWindow()->refresh(); events::killChannel(m::Mixer::PREVIEW_CHANNEL_ID, Thread::MAIN); } void setPreviewTracker(Frame f) { g_engine.model.get().getChannel(m::Mixer::PREVIEW_CHANNEL_ID).shared->tracker.store(f); g_engine.model.swap(m::model::SwapType::SOFT); previewTracker_ = f; getSampleEditorWindow()->refresh(); } void cleanupPreview() { m::Channel& channel = getChannel_(m::Mixer::PREVIEW_CHANNEL_ID); channel.samplePlayer->loadWave(*channel.shared, nullptr); g_engine.model.swap(m::model::SwapType::SOFT); } /* -------------------------------------------------------------------------- */ void toNewChannel(ID channelId, Frame a, Frame b) { ID columnId = g_ui.mainWindow->keyboard->getChannelColumnId(channelId); g_engine.mixerHandler.addAndLoadChannel(columnId, g_engine.waveManager.createFromWave(getWave_(channelId), a, b), g_engine.kernelAudio.getBufferSize(), g_engine.channelManager); } /* -------------------------------------------------------------------------- */ bool isWaveBufferFull() { return waveBuffer_ != nullptr; } /* -------------------------------------------------------------------------- */ void reload(ID channelId) { if (!v::gdConfirmWin(g_ui.langMapper.get(v::LangMap::COMMON_WARNING), "Reload sample: are you sure?")) return; if (channel::loadChannel(channelId, getWave_(channelId).getPath()) != G_RES_OK) { v::gdAlert("Unable to reload sample!"); return; } getSampleEditorWindow()->rebuild(); } /* -------------------------------------------------------------------------- */ void shift(ID channelId, Frame offset) { Frame shift = getSamplePlayer_(channelId).shift; m::model::DataLock lock = g_engine.model.lockData(); m::wfx::shift(getWave_(channelId), offset - shift); getSamplePlayer_(channelId).shift = offset; getSampleEditorWindow()->shiftTool->update(offset); } } // namespace giada::c::sampleEditor giada-0.22.0/src/glue/sampleEditor.h000066400000000000000000000062331425106661500171760ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_GLUE_SAMPLE_EDITOR_H #define G_GLUE_SAMPLE_EDITOR_H #include "core/types.h" #include "core/waveFx.h" #include #include namespace giada::m { class Wave; class Channel; } // namespace giada::m namespace giada::v { class gdSampleEditor; } namespace giada::c::sampleEditor { struct Data { Data() = default; Data(const m::Channel&); ChannelStatus a_getPreviewStatus() const; Frame a_getPreviewTracker() const; const m::Wave& getWaveRef() const; // TODO - getWaveData (or public ptr member to Wave::data) Frame getFramesInBar() const; Frame getFramesInLoop() const; 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* m_channel; }; /* onRefresh --- TODO - wrong name */ void onRefresh(Thread, std::function f); /* getData Returns a Data object filled with data from a channel. */ Data getData(ID channelId); /* setBeginEnd Sets start/end points in the sample editor. */ void setBeginEnd(ID channelId, Frame b, Frame e); 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, 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(); void playPreview(bool loop); void stopPreview(); void setPreviewTracker(Frame f); void cleanupPreview(); /* toNewChannel Copies the selected range into a new sample channel. */ void toNewChannel(ID channelId, Frame a, Frame b); } // namespace giada::c::sampleEditor #endif giada-0.22.0/src/glue/storage.cpp000066400000000000000000000152661425106661500165530ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/model/storage.h" #include "channel.h" #include "core/conf.h" #include "core/engine.h" #include "core/init.h" #include "core/mixer.h" #include "core/mixerHandler.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/sequencer.h" #include "core/wave.h" #include "core/waveManager.h" #include "glue/layout.h" #include "glue/main.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/ui.h" #include "src/core/actions/actionRecorder.h" #include "storage.h" #include "utils/fs.h" #include "utils/gui.h" #include "utils/log.h" #include "utils/string.h" #include extern giada::m::Engine g_engine; extern giada::v::Ui g_ui; namespace giada::c::storage { void loadProject(void* data) { v::gdBrowserLoad* browser = static_cast(data); const std::string projectPath = browser->getSelectedItem(); const std::string patchPath = u::fs::join(projectPath, u::fs::stripExt(u::fs::basename(projectPath)) + ".gptc"); auto progress = g_ui.mainWindow->getScopedProgress(g_ui.langMapper.get(v::LangMap::MESSAGE_STORAGE_LOADINGPROJECT)); auto progressCb = [&p = progress.get()](float v) { p.setProgress(v); }; /* Close all sub-windows first, in case there are VST editors visible. VST editors must be closed before deleting their plug-in processors. */ g_ui.closeAllSubwindows(); m::LoadState state = g_engine.load(projectPath, patchPath, progressCb); if (state.patch != G_FILE_OK) { if (state.patch == G_FILE_UNREADABLE) v::gdAlert(g_ui.langMapper.get(v::LangMap::MESSAGE_STORAGE_PATCHUNREADABLE)); else if (state.patch == G_FILE_INVALID) v::gdAlert(g_ui.langMapper.get(v::LangMap::MESSAGE_STORAGE_PATCHINVALID)); else if (state.patch == G_FILE_UNSUPPORTED) v::gdAlert(g_ui.langMapper.get(v::LangMap::MESSAGE_STORAGE_PATCHUNSUPPORTED)); return; } /* Update UI. */ g_ui.load(g_engine.patch.data); if (!state.isGood()) layout::openMissingAssetsWindow(state); browser->do_callback(); } /* -------------------------------------------------------------------------- */ void saveProject(void* data) { v::gdBrowserSave* browser = static_cast(data); const std::string projectName = u::fs::stripExt(browser->getName()); const std::string projectPath = u::fs::join(browser->getCurrentPath(), projectName + ".gprj"); const std::string patchPath = u::fs::join(projectPath, projectName + ".gptc"); if (projectName == "") { v::gdAlert(g_ui.langMapper.get(v::LangMap::MESSAGE_STORAGE_CHOOSEPROJECTNAME)); return; } if (u::fs::dirExists(projectPath) && !v::gdConfirmWin(g_ui.langMapper.get(v::LangMap::COMMON_WARNING), g_ui.langMapper.get(v::LangMap::MESSAGE_STORAGE_PROJECTEXISTS))) return; auto progress = g_ui.mainWindow->getScopedProgress(g_ui.langMapper.get(v::LangMap::MESSAGE_STORAGE_SAVINGPROJECT)); auto progressCb = [&p = progress.get()](float v) { p.setProgress(v); }; g_ui.store(projectName, g_engine.patch.data); if (!g_engine.store(projectName, projectPath, patchPath, progressCb)) { v::gdAlert(g_ui.langMapper.get(v::LangMap::MESSAGE_STORAGE_SAVINGPROJECTERROR)); return; } browser->do_callback(); } /* -------------------------------------------------------------------------- */ void loadSample(void* data) { v::gdBrowserLoad* browser = static_cast(data); std::string fullPath = browser->getSelectedItem(); if (fullPath.empty()) return; auto progress = g_ui.mainWindow->getScopedProgress(g_ui.langMapper.get(v::LangMap::MESSAGE_STORAGE_LOADINGSAMPLE)); if (int res = c::channel::loadChannel(browser->getChannelId(), fullPath); res == G_RES_OK) { g_engine.conf.data.samplePath = u::fs::dirname(fullPath); browser->do_callback(); g_ui.mainWindow->delSubWindow(WID_SAMPLE_EDITOR); // if editor is open } } /* -------------------------------------------------------------------------- */ void saveSample(void* data) { v::gdBrowserSave* browser = static_cast(data); std::string name = browser->getName(); std::string folderPath = browser->getCurrentPath(); ID channelId = browser->getChannelId(); if (name == "") { v::gdAlert(g_ui.langMapper.get(v::LangMap::MESSAGE_STORAGE_CHOOSEFILENAME)); return; } std::string filePath = u::fs::join(folderPath, u::fs::stripExt(name) + ".wav"); if (u::fs::fileExists(filePath) && !v::gdConfirmWin(g_ui.langMapper.get(v::LangMap::COMMON_WARNING), g_ui.langMapper.get(v::LangMap::MESSAGE_STORAGE_FILEEXISTS))) return; ID waveId = g_engine.model.get().getChannel(channelId).samplePlayer->getWaveId(); m::Wave* wave = g_engine.model.findShared(waveId); assert(wave != nullptr); if (!g_engine.waveManager.save(*wave, filePath)) { v::gdAlert(g_ui.langMapper.get(v::LangMap::MESSAGE_STORAGE_SAVINGFILEERROR)); return; } u::log::print("[saveSample] sample saved to %s\n", filePath); /* Update last used path in conf, so that it can be reused next time. */ g_engine.conf.data.samplePath = u::fs::dirname(filePath); /* Update logical and edited states in Wave. */ m::model::DataLock lock = g_engine.model.lockData(); wave->setLogical(false); wave->setEdited(false); /* Finally close the browser. */ browser->do_callback(); } } // namespace giada::c::storagegiada-0.22.0/src/glue/storage.h000066400000000000000000000025451425106661500162140ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_GLUE_STORAGE_H #define G_GLUE_STORAGE_H namespace giada::c::storage { void loadProject(void* data); void saveProject(void* data); void saveSample(void* data); void loadSample(void* data); } // namespace giada::c::storage #endif giada-0.22.0/src/gui/000077500000000000000000000000001425106661500142215ustar00rootroot00000000000000giada-0.22.0/src/gui/dialogs/000077500000000000000000000000001425106661500156435ustar00rootroot00000000000000giada-0.22.0/src/gui/dialogs/about.cpp000066400000000000000000000064141425106661500174660ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/conf.h" #include "core/const.h" #include "core/graphics.h" #include #include #ifdef WITH_VST #include "deps/juce-config.h" #endif #include "gui/dialogs/about.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/flex.h" #include "gui/ui.h" #include "utils/gui.h" #include "utils/string.h" #include extern giada::v::Ui g_ui; namespace giada::v { gdAbout::gdAbout() #ifdef WITH_VST : gdWindow(340, 415, g_ui.langMapper.get(LangMap::ABOUT_TITLE)) #else : gdWindow(340, 330, g_ui.langMapper.get(LangMap::ABOUT_TITLE)) #endif { #ifdef G_DEBUG_MODE constexpr bool debug = true; #else constexpr bool debug = false; #endif geFlex* container = new geFlex(getContentBounds().reduced({G_GUI_OUTER_MARGIN}), Direction::VERTICAL, G_GUI_OUTER_MARGIN); { geFlex* body = new geFlex(Direction::VERTICAL); { geBox* logo = new geBox(); logo->image(new Fl_Pixmap(giada_logo_xpm)); geBox* text = new geBox(); text->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_TOP); text->copy_label(fmt::format(g_ui.langMapper.get(LangMap::ABOUT_BODY), G_VERSION_STR, debug ? "Debug" : "Release", BUILD_DATE) .c_str()); #ifdef WITH_VST geBox* vstLogo = new geBox(); vstLogo->image(new Fl_Pixmap(vstLogo_xpm)); vstLogo->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE); geBox* vstText = new geBox(); vstText->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_TOP); vstText->label(g_ui.langMapper.get(LangMap::ABOUT_BODY_VST)); #endif body->add(logo, 120); body->add(text, 140); #ifdef WITH_VST body->add(vstLogo, 60); body->add(vstText); #endif body->end(); } geFlex* footer = new geFlex(Direction::HORIZONTAL); { geButton* close = new geButton(g_ui.langMapper.get(LangMap::COMMON_CLOSE)); close->onClick = [this]() { do_callback(); }; footer->add(new geBox()); // Spacer footer->add(close, 80); footer->end(); } container->add(body); container->add(footer, G_GUI_UNIT); container->end(); } add(container); set_modal(); u::gui::setFavicon(this); setId(WID_ABOUT); show(); } } // namespace giada::vgiada-0.22.0/src/gui/dialogs/about.h000066400000000000000000000024531425106661500171320ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_ABOUT_H #define GD_ABOUT_H #include "gui/dialogs/window.h" namespace giada::v { class gdAbout : public gdWindow { public: gdAbout(); }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/actionEditor/000077500000000000000000000000001425106661500202675ustar00rootroot00000000000000giada-0.22.0/src/gui/dialogs/actionEditor/baseActionEditor.cpp000066400000000000000000000154731425106661500242240ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/actionEditor/baseActionEditor.h" #include "core/conf.h" #include "core/const.h" #include "core/graphics.h" #include "glue/channel.h" #include "gui/drawing.h" #include "gui/elems/actionEditor/gridTool.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/choice.h" #include "gui/elems/basics/scrollPack.h" #include "gui/ui.h" #include "src/core/actions/action.h" #include "utils/gui.h" #include "utils/string.h" #include #include #include #include #include extern giada::v::Ui g_ui; namespace giada::v { gdBaseActionEditor::gdBaseActionEditor(ID channelId, m::Conf::Data& conf) : gdWindow(conf.actionEditorX, conf.actionEditorY, conf.actionEditorW, conf.actionEditorH, g_ui.langMapper.get(LangMap::ACTIONEDITOR_TITLE)) , channelId(channelId) , gridTool(0, 0, conf) , zoomInBtn(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", zoomInOff_xpm, zoomInOn_xpm) , zoomOutBtn(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", zoomOutOff_xpm, zoomOutOn_xpm) , m_barTop(0, 0, Direction::HORIZONTAL) , m_splitScroll(0, 0, 0, 0) , m_conf(conf) , m_ratio(conf.actionEditorZoom) { end(); m_barTop.position(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN); m_splitScroll.resize( G_GUI_OUTER_MARGIN, (G_GUI_OUTER_MARGIN * 2) + 20, w() - G_GUI_OUTER_MARGIN * 2, h() - (G_GUI_OUTER_MARGIN * 3) - 20); zoomInBtn.callback(cb_zoomIn, this); zoomInBtn.copy_tooltip(g_ui.langMapper.get(LangMap::COMMON_ZOOMIN)); zoomOutBtn.callback(cb_zoomOut, this); zoomOutBtn.copy_tooltip(g_ui.langMapper.get(LangMap::COMMON_ZOOMOUT)); add(m_barTop); add(m_splitScroll); } /* -------------------------------------------------------------------------- */ gdBaseActionEditor::~gdBaseActionEditor() { using namespace giada::m; m_conf.actionEditorX = x(); m_conf.actionEditorY = y(); m_conf.actionEditorW = w(); m_conf.actionEditorH = h(); m_conf.actionEditorSplitH = m_splitScroll.getTopContentH(); m_conf.actionEditorZoom = m_ratio; } /* -------------------------------------------------------------------------- */ int gdBaseActionEditor::getMouseOverContent() const { return m_splitScroll.getScrollX() + (Fl::event_x() - G_GUI_OUTER_MARGIN); } /* -------------------------------------------------------------------------- */ 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(Frame framesInSeq, Frame framesInLoop) { fullWidth = frameToPixel(framesInSeq); loopWidth = frameToPixel(framesInLoop); } /* -------------------------------------------------------------------------- */ Pixel gdBaseActionEditor::frameToPixel(Frame f) const { return f / m_ratio; } Frame gdBaseActionEditor::pixelToFrame(Pixel p, Frame framesInBeat, bool snap) const { return snap ? gridTool.getSnapFrame(p * m_ratio, framesInBeat) : p * m_ratio; } /* -------------------------------------------------------------------------- */ void gdBaseActionEditor::zoomIn() { // Explicit type std::max to fix MINMAX macro hell on Windows zoomAbout([&r = m_ratio]() { return std::max(r / RATIO_STEP, MIN_RATIO); }); } /* -------------------------------------------------------------------------- */ void gdBaseActionEditor::zoomOut() { // Explicit type std::max to fix MINMAX macro hell on Windows zoomAbout([&r = m_ratio]() { return std::min(r * RATIO_STEP, MAX_RATIO); }); } /* -------------------------------------------------------------------------- */ void gdBaseActionEditor::prepareWindow() { u::gui::setFavicon(this); set_non_modal(); size_range(640, 284); 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); } } /* -------------------------------------------------------------------------- */ void gdBaseActionEditor::draw() { gdWindow::draw(); const geompp::Rect splitBounds = m_splitScroll.getBoundsNoScrollbar(); const geompp::Line playhead = splitBounds.getHeightAsLine().withX(currentFrameToPixel()); if (splitBounds.contains(playhead)) drawLine(playhead, G_COLOR_LIGHT_2); } /* -------------------------------------------------------------------------- */ void gdBaseActionEditor::zoomAbout(std::function f) { const float ratioPrev = m_ratio; const int minWidth = w() - (G_GUI_OUTER_MARGIN * 2); m_ratio = f(); /* Make sure the new content width doesn't underflow the window space (i.e. the minimum width allowed). */ if (frameToPixel(m_data.framesInSeq) < minWidth) { m_ratio = m_data.framesInSeq / static_cast(minWidth); m_splitScroll.setScrollX(0); } /* 1. Store the current x-position, then the new x-position affected by the zoom change. */ const int mpre = getMouseOverContent(); const int mnow = mpre / (m_ratio / ratioPrev); /* 2. Rebuild everything and adjust scrolling given the change occurred in the x-position. This effectively centers the view on the mouse cursor. */ rebuild(); m_splitScroll.setScrollX(m_splitScroll.getScrollX() + (mnow - mpre)); redraw(); } /* -------------------------------------------------------------------------- */ void gdBaseActionEditor::refresh() { redraw(); } /* -------------------------------------------------------------------------- */ Pixel gdBaseActionEditor::currentFrameToPixel() const { return (frameToPixel(m_data.getCurrentFrame()) + m_splitScroll.x()) - m_splitScroll.getScrollX(); } } // namespace giada::vgiada-0.22.0/src/gui/dialogs/actionEditor/baseActionEditor.h000066400000000000000000000060471425106661500236660ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_BASE_ACTION_EDITOR_H #define GD_BASE_ACTION_EDITOR_H #include "core/conf.h" #include "glue/actionEditor.h" #include "gui/dialogs/window.h" #include "gui/elems/actionEditor/gridTool.h" #include "gui/elems/actionEditor/splitScroll.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/pack.h" namespace giada::m { class Channel; struct Action; } // namespace giada::m namespace giada::v { class gdBaseActionEditor : public gdWindow { public: virtual ~gdBaseActionEditor(); int handle(int e) override; void draw() override; Pixel frameToPixel(Frame f) const; Frame pixelToFrame(Pixel p, Frame framesInBeat, bool snap = true) const; ID channelId; geGridTool gridTool; geButton zoomInBtn; geButton zoomOutBtn; Pixel fullWidth; // Full widgets width, i.e. scaled-down full sequencer Pixel loopWidth; // Loop width, i.e. scaled-down sequencer range protected: static constexpr float MIN_RATIO = 25.0f; static constexpr float MAX_RATIO = 40000.0f; static constexpr float RATIO_STEP = 1.5f; gdBaseActionEditor(ID channelId, m::Conf::Data&); /* getMouseOverContent Returns mouse x-position relative to the viewport content. */ int getMouseOverContent() const; static void cb_zoomIn(Fl_Widget* w, void* p); static void cb_zoomOut(Fl_Widget* w, void* p); void zoomIn(); void zoomOut(); /* computeWidth Computes total width, in pixel. */ void computeWidth(Frame framesInSeq, Frame framesInLoop); /* prepareWindow Initializes window (favicon, limits, ...). */ void prepareWindow(); gePack m_barTop; geSplitScroll m_splitScroll; c::actionEditor::Data m_data; m::Conf::Data& m_conf; private: void refresh() override; /* zoomAbout Zooms and centers the viewport around the mouse cursor. Wants a function to apply to the current ratio. */ void zoomAbout(std::function f); Pixel currentFrameToPixel() const; float m_ratio; }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/actionEditor/midiActionEditor.cpp000066400000000000000000000050031425106661500242200ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "midiActionEditor.h" #include "core/conf.h" #include "glue/actionEditor.h" #include "glue/channel.h" #include "gui/elems/basics/box.h" namespace giada::v { gdMidiActionEditor::gdMidiActionEditor(ID channelId, m::Conf::Data& conf) : gdBaseActionEditor(channelId, conf) , m_barPadding(0, 0, w() - 150, G_GUI_UNIT) , m_pianoRoll(0, 0, this) , m_velocityEditor(0, 0, this) { end(); m_barTop.add(&gridTool); m_barTop.add(&m_barPadding); m_barTop.add(&zoomInBtn); m_barTop.add(&zoomOutBtn); m_barTop.resizable(m_barPadding); m_splitScroll.addWidgets(m_pianoRoll, m_velocityEditor, conf.actionEditorSplitH); if (conf.actionEditorPianoRollY != -1) m_splitScroll.setScrollY(conf.actionEditorPianoRollY); resizable(m_splitScroll); // Make it resizable only once filled with widgets prepareWindow(); rebuild(); } /* -------------------------------------------------------------------------- */ gdMidiActionEditor::~gdMidiActionEditor() { m_conf.actionEditorPianoRollY = m_splitScroll.getScrollY(); m_barTop.remove(gridTool); m_barTop.remove(zoomInBtn); m_barTop.remove(zoomOutBtn); } /* -------------------------------------------------------------------------- */ void gdMidiActionEditor::rebuild() { m_data = c::actionEditor::getData(channelId); computeWidth(m_data.framesInSeq, m_data.framesInLoop); m_pianoRoll.rebuild(m_data); m_velocityEditor.rebuild(m_data); } } // namespace giada::vgiada-0.22.0/src/gui/dialogs/actionEditor/midiActionEditor.h000066400000000000000000000032421425106661500236700ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_MIDI_ACTION_EDITOR_H #define GD_MIDI_ACTION_EDITOR_H #include "baseActionEditor.h" #include "gui/elems/actionEditor/pianoRoll.h" #include "gui/elems/actionEditor/velocityEditor.h" #include "gui/elems/basics/box.h" namespace giada::v { class gdMidiActionEditor : public gdBaseActionEditor { public: gdMidiActionEditor(ID channelId, m::Conf::Data&); ~gdMidiActionEditor(); void rebuild() override; private: geBox m_barPadding; gePianoRoll m_pianoRoll; geVelocityEditor m_velocityEditor; }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/actionEditor/sampleActionEditor.cpp000066400000000000000000000074671425106661500245770ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/actionEditor/sampleActionEditor.h" #include "core/conf.h" #include "core/const.h" #include "core/midiEvent.h" #include "core/model/model.h" #include "glue/actionEditor.h" #include "glue/channel.h" #include "gui/elems/basics/box.h" #include "gui/ui.h" #include extern giada::v::Ui g_ui; namespace giada::v { gdSampleActionEditor::gdSampleActionEditor(ID channelId, m::Conf::Data& conf) : gdBaseActionEditor(channelId, conf) , m_barPadding(0, 0, w() - 232, G_GUI_UNIT) , m_sampleActionEditor(0, 0, this) , m_envelopeEditor(0, 0, g_ui.langMapper.get(LangMap::ACTIONEDITOR_VOLUME), this) , m_actionType(0, 0, 80, G_GUI_UNIT) { end(); m_barTop.add(&m_actionType); m_barTop.add(&gridTool); m_barTop.add(&m_barPadding); m_barTop.add(&zoomInBtn); m_barTop.add(&zoomOutBtn); m_barTop.resizable(m_barPadding); m_actionType.addItem(g_ui.langMapper.get(LangMap::ACTIONEDITOR_KEYPRESS)); m_actionType.addItem(g_ui.langMapper.get(LangMap::ACTIONEDITOR_KEYRELEASE)); m_actionType.addItem(g_ui.langMapper.get(LangMap::ACTIONEDITOR_STOPSAMPLE)); m_actionType.showItem(0); m_actionType.copy_tooltip(g_ui.langMapper.get(LangMap::ACTIONEDITOR_LABEL_ACTIONTYPE)); if (!canChangeActionType()) m_actionType.deactivate(); m_splitScroll.addWidgets(m_sampleActionEditor, m_envelopeEditor, conf.actionEditorSplitH); resizable(m_splitScroll); // Make it resizable only once filled with widgets prepareWindow(); rebuild(); } /* -------------------------------------------------------------------------- */ gdSampleActionEditor::~gdSampleActionEditor() { m_barTop.remove(m_actionType); m_barTop.remove(gridTool); m_barTop.remove(zoomInBtn); m_barTop.remove(zoomOutBtn); } /* -------------------------------------------------------------------------- */ bool gdSampleActionEditor::canChangeActionType() { return m_data.sample->channelMode != SamplePlayerMode::SINGLE_PRESS && m_data.sample->isLoopMode == false; } /* -------------------------------------------------------------------------- */ void gdSampleActionEditor::rebuild() { m_data = c::actionEditor::getData(channelId); canChangeActionType() ? m_actionType.activate() : m_actionType.deactivate(); computeWidth(m_data.framesInSeq, m_data.framesInLoop); m_sampleActionEditor.rebuild(m_data); m_envelopeEditor.rebuild(m_data); } /* -------------------------------------------------------------------------- */ int gdSampleActionEditor::getActionType() const { if (m_actionType.getSelectedId() == 0) return m::MidiEvent::NOTE_ON; else if (m_actionType.getSelectedId() == 1) return m::MidiEvent::NOTE_OFF; else if (m_actionType.getSelectedId() == 2) return m::MidiEvent::NOTE_KILL; assert(false); return -1; } } // namespace giada::vgiada-0.22.0/src/gui/dialogs/actionEditor/sampleActionEditor.h000066400000000000000000000035161425106661500242330ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_SAMPLE_ACTION_EDITOR_H #define GD_SAMPLE_ACTION_EDITOR_H #include "baseActionEditor.h" #include "gui/elems/actionEditor/envelopeEditor.h" #include "gui/elems/actionEditor/sampleActionEditor.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/choice.h" namespace giada::v { class gdSampleActionEditor : public gdBaseActionEditor { public: gdSampleActionEditor(ID channelId, m::Conf::Data&); ~gdSampleActionEditor(); void rebuild() override; int getActionType() const; private: bool canChangeActionType(); geBox m_barPadding; geSampleActionEditor m_sampleActionEditor; geEnvelopeEditor m_envelopeEditor; geChoice m_actionType; }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/beatsInput.cpp000066400000000000000000000047441425106661500204760ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/beatsInput.h" #include "core/const.h" #include "glue/main.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/check.h" #include "gui/elems/basics/flex.h" #include "gui/elems/basics/input.h" #include "gui/ui.h" #include "utils/gui.h" #include "utils/string.h" #include extern giada::v::Ui g_ui; namespace giada::v { gdBeatsInput::gdBeatsInput(int beats, int bars) : gdWindow(u::gui::getCenterWinBounds(180, 36), "Beats") { geFlex* container = new geFlex(getContentBounds().reduced({G_GUI_OUTER_MARGIN}), Direction::HORIZONTAL, G_GUI_INNER_MARGIN); { m_beats = new geInput(0, 0, 0, 0); m_bars = new geInput(0, 0, 0, 0); m_ok = new geButton(g_ui.langMapper.get(LangMap::COMMON_OK)); container->add(m_beats); container->add(m_bars); container->add(m_ok, 70); container->end(); } add(container); m_beats->maximum_size(2); m_beats->value(std::to_string(beats).c_str()); m_beats->type(FL_INT_INPUT); m_bars->maximum_size(2); m_bars->value(std::to_string(bars).c_str()); m_bars->type(FL_INT_INPUT); m_ok->shortcut(FL_Enter); m_ok->onClick = [this]() { if (!strcmp(m_beats->value(), "") || !strcmp(m_bars->value(), "")) return; c::main::setBeats(atoi(m_beats->value()), atoi(m_bars->value())); do_callback(); }; set_modal(); u::gui::setFavicon(this); setId(WID_BEATS); show(); } } // namespace giada::vgiada-0.22.0/src/gui/dialogs/beatsInput.h000066400000000000000000000026501425106661500201350ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_BEATSINPUT_H #define GD_BEATSINPUT_H #include "window.h" class geInput; namespace giada::v { class geButton; class gdBeatsInput : public gdWindow { public: gdBeatsInput(int beats, int bars); private: geInput* m_beats; geInput* m_bars; geButton* m_ok; }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/bpmInput.cpp000066400000000000000000000050021425106661500201420ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/bpmInput.h" #include "core/conf.h" #include "core/const.h" #include "core/mixer.h" #include "glue/main.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/flex.h" #include "gui/elems/basics/input.h" #include "gui/ui.h" #include "utils/gui.h" #include "utils/string.h" #include extern giada::v::Ui g_ui; namespace giada::v { gdBpmInput::gdBpmInput(const char* label) : gdWindow(u::gui::getCenterWinBounds(180, 36), "Bpm") { geFlex* container = new geFlex(getContentBounds().reduced({G_GUI_OUTER_MARGIN}), Direction::HORIZONTAL, G_GUI_INNER_MARGIN); { m_input_a = new geInput(0, 0, 0, 0); m_input_b = new geInput(0, 0, 0, 0); m_ok = new geButton(g_ui.langMapper.get(LangMap::COMMON_OK)); container->add(m_input_a); container->add(m_input_b); container->add(m_ok, 70); container->end(); } add(container); std::vector parts = u::string::split(label, "."); m_input_a->maximum_size(3); m_input_a->type(FL_INT_INPUT); m_input_a->value(parts[0].c_str()); m_input_b->maximum_size(1); m_input_b->type(FL_INT_INPUT); m_input_b->value(parts[1].c_str()); m_ok->shortcut(FL_Enter); m_ok->onClick = [this]() { if (strcmp(m_input_a->value(), "") == 0) return; c::main::setBpm(m_input_a->value(), m_input_b->value()); do_callback(); }; set_modal(); u::gui::setFavicon(this); setId(WID_BPM); show(); } } // namespace giada::vgiada-0.22.0/src/gui/dialogs/bpmInput.h000066400000000000000000000027171425106661500176210ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_BPMINPUT_H #define GD_BPMINPUT_H #include "window.h" class geInput; namespace giada::v { class geButton; class gdBpmInput : public gdWindow { public: gdBpmInput(const char* label); // pointer to mainWin->timing->bpm->label() private: geInput* m_input_a; geInput* m_input_b; geButton* m_ok; }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/browser/000077500000000000000000000000001425106661500173265ustar00rootroot00000000000000giada-0.22.0/src/gui/dialogs/browser/browserBase.cpp000066400000000000000000000117751425106661500223230ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/browser/browserBase.h" #include "core/conf.h" #include "core/const.h" #include "core/graphics.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/progress.h" #include "gui/elems/fileBrowser.h" #include "gui/ui.h" #include "utils/fs.h" #include "utils/gui.h" extern giada::v::Ui g_ui; namespace giada::v { gdBrowserBase::gdBrowserBase(const std::string& title, const std::string& path, std::function callback, ID channelId, m::Conf::Data& c) : gdWindow(c.browserX, c.browserY, c.browserW, c.browserH, title.c_str()) , m_callback(callback) , m_conf(c) , m_channelId(channelId) { set_non_modal(); begin(); groupTop = new Fl_Group(8, 8, w() - 16, 48); hiddenFiles = new geCheck(groupTop->x(), groupTop->y(), 400, 20, g_ui.langMapper.get(LangMap::BROWSER_SHOWHIDDENFILES)); 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); where->readonly(true); where->cursor_color(G_COLOR_BLACK); where->value(path.c_str()); updir->callback(cb_up, (void*)this); browser = new geFileBrowser(8, groupTop->y() + groupTop->h() + 8, w() - 16, h() - 101); browser->loadDir(path); if (path == m_conf.browserLastPath) browser->preselect(m_conf.browserPosition, m_conf.browserLastValue); Fl_Group* groupButtons = new Fl_Group(8, browser->y() + browser->h() + 8, w() - 16, 20); ok = new geButton(w() - 88, groupButtons->y(), 80, 20); cancel = new geButton(w() - ok->w() - 96, groupButtons->y(), 80, 20, g_ui.langMapper.get(LangMap::COMMON_CANCEL)); geBox* spacer = new geBox(8, groupButtons->y(), cancel->x() - 16, 20); groupButtons->resizable(spacer); groupButtons->end(); end(); cancel->callback(cb_close, (void*)this); resizable(browser); size_range(320, 200); u::gui::setFavicon(this); show(); } /* -------------------------------------------------------------------------- */ gdBrowserBase::~gdBrowserBase() { m_conf.browserX = x(); m_conf.browserY = y(); m_conf.browserW = w(); m_conf.browserH = h(); m_conf.browserPosition = browser->position(); m_conf.browserLastPath = browser->getCurrentDir(); m_conf.browserLastValue = browser->value(); } /* -------------------------------------------------------------------------- */ void gdBrowserBase::cb_up(Fl_Widget* /*v*/, void* p) { ((gdBrowserBase*)p)->cb_up(); } void gdBrowserBase::cb_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(); } /* -------------------------------------------------------------------------- */ std::string gdBrowserBase::getCurrentPath() const { return where->value(); } ID gdBrowserBase::getChannelId() const { return m_channelId; } std::string gdBrowserBase::getSelectedItem() const { return browser->getSelectedItem(); } /* -------------------------------------------------------------------------- */ void gdBrowserBase::fireCallback() const { m_callback((void*)this); } } // namespace giada::vgiada-0.22.0/src/gui/dialogs/browser/browserBase.h000066400000000000000000000046761425106661500217720ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_BROWSER_BASE_H #define GD_BROWSER_BASE_H #include "core/conf.h" #include "core/types.h" #include "gui/dialogs/window.h" #include #include class Fl_Group; class geCheck; class geInput; namespace giada::m { class Channel; } namespace giada::v { class geButton; class geFileBrowser; class gdBrowserBase : public gdWindow { public: ~gdBrowserBase(); /* getSelectedItem Returns the full path of the selected file. */ std::string getSelectedItem() const; std::string getCurrentPath() const; ID getChannelId() const; void fireCallback() const; protected: gdBrowserBase(const std::string& title, const std::string& path, std::function f, ID channelId, m::Conf::Data&); 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(); /* m_callback Fired when the save/load button is pressed. */ std::function m_callback; m::Conf::Data& m_conf; ID m_channelId; Fl_Group* groupTop; geCheck* hiddenFiles; geFileBrowser* browser; geButton* ok; geButton* cancel; geInput* where; geButton* updir; }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/browser/browserDir.cpp000066400000000000000000000051221425106661500221540ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/browser/browserDir.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/input.h" #include "gui/elems/fileBrowser.h" #include "gui/ui.h" #include "utils/fs.h" extern giada::v::Ui g_ui; namespace giada::v { gdBrowserDir::gdBrowserDir(const std::string& title, const std::string& path, std::function cb, m::Conf::Data& conf) : gdBrowserBase(title, path, cb, 0, conf) { where->size(groupTop->w() - updir->w() - 8, 20); browser->callback(cb_down, (void*)this); ok->label(g_ui.langMapper.get(LangMap::COMMON_SELECT)); 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(); if (path.empty() || !u::fs::isDir(path)) // when click on an empty area or not a dir return; browser->loadDir(path); where->value(browser->getCurrentDir().c_str()); } } // namespace giada::vgiada-0.22.0/src/gui/dialogs/browser/browserDir.h000066400000000000000000000031271425106661500216240ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_BROWSER_DIR_H #define GD_BROWSER_DIR_H #include "core/conf.h" #include "browserBase.h" namespace giada::v { class gdBrowserDir : public gdBrowserBase { public: gdBrowserDir(const std::string& title, const std::string& path, std::function cb, m::Conf::Data&); 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(); }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/browser/browserLoad.cpp000066400000000000000000000051571425106661500223250ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/browser/browserLoad.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/input.h" #include "gui/elems/fileBrowser.h" #include "gui/ui.h" #include "utils/fs.h" extern giada::v::Ui g_ui; namespace giada::v { gdBrowserLoad::gdBrowserLoad(const std::string& title, const std::string& path, std::function cb, ID channelId, m::Conf::Data& conf) : gdBrowserBase(title, path, cb, channelId, conf) { where->size(groupTop->w() - updir->w() - 8, 20); browser->callback(cb_down, (void*)this); ok->label(g_ui.langMapper.get(LangMap::COMMON_LOAD)); 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(); if (path.empty() || !u::fs::isDir(path)) // when click on an empty area or not a dir return; browser->loadDir(path); where->value(browser->getCurrentDir().c_str()); } } // namespace giada::vgiada-0.22.0/src/gui/dialogs/browser/browserLoad.h000066400000000000000000000032201425106661500217570ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_BROWSER_LOAD_H #define GD_BROWSER_LOAD_H #include "browserBase.h" #include "core/conf.h" namespace giada::m { class Channel; } namespace giada::v { class gdBrowserLoad : public gdBrowserBase { public: gdBrowserLoad(const std::string& title, const std::string& path, std::function cb, ID channelId, m::Conf::Data&); 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(); }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/browser/browserSave.cpp000066400000000000000000000061671425106661500223460ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/browser/browserSave.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/input.h" #include "gui/elems/fileBrowser.h" #include "gui/ui.h" #include "utils/fs.h" extern giada::v::Ui g_ui; namespace giada::v { gdBrowserSave::gdBrowserSave(const std::string& title, const std::string& path, const std::string& name_, std::function cb, ID channelId, m::Conf::Data& conf) : gdBrowserBase(title, path, cb, channelId, conf) { where->size(groupTop->w() - 236, 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); ok->label(g_ui.langMapper.get(LangMap::COMMON_SAVE)); 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. Let's force them. */ where->redraw(); 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 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)) { browser->loadDir(path); where->value(browser->getCurrentDir().c_str()); } else name->value(browser->getSelectedItem(false).c_str()); } /* -------------------------------------------------------------------------- */ std::string gdBrowserSave::getName() const { return name->value(); } /* -------------------------------------------------------------------------- */ void gdBrowserSave::cb_save() { fireCallback(); } } // namespace giada::vgiada-0.22.0/src/gui/dialogs/browser/browserSave.h000066400000000000000000000033561425106661500220100ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_BROWSER_SAVE_H #define GD_BROWSER_SAVE_H #include "core/conf.h" #include "browserBase.h" class geInput; namespace giada::m { class Channel; } namespace giada::v { class gdBrowserSave : public gdBrowserBase { public: gdBrowserSave(const std::string& title, const std::string& path, const std::string& name, std::function cb, ID channelId, m::Conf::Data&); std::string getName() const; private: 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(); }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/channelNameInput.cpp000066400000000000000000000050401425106661500215770ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/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/flex.h" #include "gui/elems/basics/input.h" #include "gui/ui.h" #include "utils/gui.h" extern giada::v::Ui g_ui; namespace giada::v { gdChannelNameInput::gdChannelNameInput(const c::channel::Data& d) : gdWindow(u::gui::getCenterWinBounds(400, 64), g_ui.langMapper.get(LangMap::CHANNELNAME_TITLE)) , m_data(d) { geFlex* container = new geFlex(getContentBounds().reduced({G_GUI_OUTER_MARGIN}), Direction::VERTICAL, G_GUI_OUTER_MARGIN); { m_name = new geInput(0, 0, 0, 0); geFlex* footer = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN); { m_ok = new geButton(g_ui.langMapper.get(LangMap::COMMON_OK)); m_cancel = new geButton(g_ui.langMapper.get(LangMap::COMMON_CANCEL)); footer->add(new geBox()); footer->add(m_cancel, 70); footer->add(m_ok, 70); footer->end(); } container->add(m_name, G_GUI_UNIT); container->add(footer, G_GUI_UNIT); container->end(); } add(container); m_name->value(m_data.name.c_str()); m_ok->shortcut(FL_Enter); m_ok->onClick = [this]() { c::channel::setName(m_data.id, m_name->value()); do_callback(); }; m_cancel->onClick = [this]() { do_callback(); }; set_modal(); u::gui::setFavicon(this); setId(WID_SAMPLE_NAME); show(); } } // namespace giada::vgiada-0.22.0/src/gui/dialogs/channelNameInput.h000066400000000000000000000030331425106661500212440ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_CHANNEL_NAME_INPUT_H #define GD_CHANNEL_NAME_INPUT_H #include "window.h" class geInput; namespace giada::c::channel { struct Data; } namespace giada::v { class geButton; class gdChannelNameInput : public gdWindow { public: gdChannelNameInput(const c::channel::Data& d); private: const c::channel::Data& m_data; geInput* m_name; geButton* m_ok; geButton* m_cancel; }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/config.cpp000066400000000000000000000066471425106661500176310ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/config.h" #include "core/conf.h" #include "core/const.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/boxtypes.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/flex.h" #include "gui/elems/basics/tabs.h" #include "gui/elems/config/tabAudio.h" #include "gui/elems/config/tabBehaviors.h" #include "gui/elems/config/tabBindings.h" #include "gui/elems/config/tabMidi.h" #include "gui/elems/config/tabMisc.h" #include "gui/elems/config/tabPlugins.h" #include "gui/ui.h" #include "utils/gui.h" extern giada::v::Ui g_ui; namespace giada::v { gdConfig::gdConfig(int w, int h, m::Conf::Data& conf) : gdWindow(u::gui::getCenterWinBounds(w, h), g_ui.langMapper.get(LangMap::CONFIG_TITLE)) { const geompp::Rect bounds = getContentBounds().reduced(G_GUI_OUTER_MARGIN); geFlex* container = new geFlex(bounds, Direction::VERTICAL, G_GUI_OUTER_MARGIN); { geTabs* tabs = new geTabs(bounds); { tabAudio = new geTabAudio(bounds); tabMidi = new geTabMidi(bounds); tabBehaviors = new geTabBehaviors(bounds, conf); tabMisc = new geTabMisc(bounds); tabBindings = new geTabBindings(bounds, conf); #ifdef WITH_VST tabPlugins = new geTabPlugins(bounds); #endif tabs->add(tabAudio); tabs->add(tabMidi); tabs->add(tabBehaviors); #ifdef WITH_VST tabs->add(tabPlugins); #endif tabs->add(tabBindings); tabs->add(tabMisc); } geFlex* footer = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN); { geButton* save = new geButton(g_ui.langMapper.get(LangMap::COMMON_SAVE)); geButton* cancel = new geButton(g_ui.langMapper.get(LangMap::COMMON_CANCEL)); save->onClick = [this]() { saveConfig(); }; cancel->onClick = [this]() { do_callback(); }; footer->add(new geBox()); // Spacer footer->add(cancel, 80); footer->add(save, 80); footer->end(); } container->add(tabs); container->add(footer, G_GUI_UNIT); container->end(); } add(container); resizable(container); size_range(w, h); u::gui::setFavicon(this); setId(WID_CONFIG); show(); } /* -------------------------------------------------------------------------- */ void gdConfig::saveConfig() { tabAudio->save(); tabBehaviors->save(); tabMidi->save(); tabMisc->save(); #ifdef WITH_VST tabPlugins->save(); #endif do_callback(); } } // namespace giada::v giada-0.22.0/src/gui/dialogs/config.h000066400000000000000000000033151425106661500172630ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_CONFIG_H #define GD_CONFIG_H #include "core/conf.h" #include "window.h" namespace giada::v { class geButton; class geTabAudio; class geTabBehaviors; class geTabMidi; class geTabMisc; class geTabBindings; #ifdef WITH_VST class geTabPlugins; #endif class gdConfig : public gdWindow { public: gdConfig(int w, int h, m::Conf::Data&); geTabAudio* tabAudio; geTabBehaviors* tabBehaviors; geTabMidi* tabMidi; geTabMisc* tabMisc; geTabBindings* tabBindings; #ifdef WITH_VST geTabPlugins* tabPlugins; #endif private: void saveConfig(); }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/keyGrabber.cpp000066400000000000000000000061371425106661500204330ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/keyGrabber.h" #include "core/conf.h" #include "glue/channel.h" #include "glue/io.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/flex.h" #include "gui/ui.h" #include "utils/gui.h" #include "utils/log.h" #include "utils/string.h" #include extern giada::v::Ui g_ui; namespace giada::v { gdKeyGrabber::gdKeyGrabber(int key) : gdWindow(300, 126, g_ui.langMapper.get(LangMap::KEYGRABBER_TITLE)) , onSetKey(nullptr) , m_key(key) { geFlex* container = new geFlex(getContentBounds().reduced({G_GUI_OUTER_MARGIN}), Direction::VERTICAL, G_GUI_OUTER_MARGIN); { m_text = new geBox(); geFlex* footer = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN); { m_clear = new geButton(g_ui.langMapper.get(LangMap::COMMON_CLEAR)); m_cancel = new geButton(g_ui.langMapper.get(LangMap::COMMON_CLOSE)); footer->add(new geBox()); // Spacer footer->add(m_clear, 80); footer->add(m_cancel, 80); footer->end(); } container->add(m_text); container->add(footer, G_GUI_UNIT); container->end(); } add(container); m_clear->onClick = [this]() { assert(onSetKey != nullptr); m_key = 0; onSetKey(m_key); rebuild(); }; m_cancel->onClick = [this]() { do_callback(); }; rebuild(); u::gui::setFavicon(this); set_modal(); show(); } /* -------------------------------------------------------------------------- */ void gdKeyGrabber::rebuild() { std::string tmp = g_ui.langMapper.get(LangMap::KEYGRABBER_BODY) + u::gui::keyToString(m_key); m_text->copy_label(tmp.c_str()); } /* -------------------------------------------------------------------------- */ int gdKeyGrabber::handle(int e) { if (e != FL_KEYUP) return Fl_Group::handle(e); assert(onSetKey != nullptr); const int newKey = Fl::event_key(); if (!onSetKey(newKey)) { u::log::print("Invalid key\n"); return 1; } m_key = newKey; rebuild(); u::log::print("Set key '%c' (%d)\n", m_key, m_key); return 1; } } // namespace giada::v giada-0.22.0/src/gui/dialogs/keyGrabber.h000066400000000000000000000034051425106661500200730ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_KEYGRABBER_H #define GD_KEYGRABBER_H #include "core/conf.h" #include "window.h" #include #include namespace giada::c::channel { struct Data; } namespace giada::v { class geBox; class geButton; class gdKeyGrabber : public gdWindow { public: gdKeyGrabber(int key); int handle(int e) override; void rebuild() override; /* onSetKey Callback fired when this widget has grabbed an event. Returns a boolean value to inform the widget if the key is valid. */ std::function onSetKey; private: int m_key; geBox* m_text; geButton* m_clear; geButton* m_cancel; }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/mainWindow.cpp000066400000000000000000000124721425106661500204710ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "mainWindow.h" #include "core/conf.h" #include "core/const.h" #include "core/init.h" #include "gui/elems/basics/boxtypes.h" #include "gui/elems/basics/flex.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/sequencer.h" #include "utils/gui.h" #include "warnings.h" #include #include namespace giada::v { gdMainWindow::ScopedProgress::ScopedProgress(gdProgress& p, const char* msg) : m_progress(p) { m_progress.popup(msg); } /* -------------------------------------------------------------------------- */ gdMainWindow::ScopedProgress::~ScopedProgress() { m_progress.hide(); } /* -------------------------------------------------------------------------- */ gdProgress& gdMainWindow::ScopedProgress::get() { return m_progress; } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ gdMainWindow::gdMainWindow(int W, int H, const char* title, int argc, char** argv, m::Conf::Data& c) : gdWindow(W, H, title) , m_conf(c) { 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(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_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.showTooltips); geFlex* container = new geFlex(getContentBounds().reduced({G_GUI_OUTER_MARGIN}), Direction::VERTICAL, G_GUI_OUTER_MARGIN); { /* zone 1 - menus, and I/O tools */ geFlex* zone1 = new geFlex(Direction::HORIZONTAL, G_GUI_INNER_MARGIN); { mainMenu = new v::geMainMenu(); mainIO = new v::geMainIO(); zone1->add(mainMenu, 350); zone1->add(new geBox()); zone1->add(mainIO, 430); zone1->end(); } /* zone 2 - mainTransport and timing tools */ geFlex* zone2 = new geFlex(Direction::HORIZONTAL, G_GUI_INNER_MARGIN); { mainTransport = new v::geMainTransport(); mainTimer = new v::geMainTimer(); zone2->add(mainTransport, 400); zone2->add(new geBox()); zone2->add(mainTimer, 237, {2, 0, 3, 0}); zone2->end(); } sequencer = new v::geSequencer(); keyboard = new v::geKeyboard(); container->add(zone1, G_GUI_UNIT); container->add(zone2, 30, {5, 0, 0, 0}); container->add(sequencer, 40, {4, 80, 4, 80}); container->add(keyboard); container->end(); } add(container); resizable(container); callback([](Fl_Widget* /*w*/, void* /*v*/) { if (Fl::event() == FL_SHORTCUT && Fl::event_key() == FL_Escape) return; // ignore Escape m::init::closeMainWindow(); }); u::gui::setFavicon(this); size_range(G_MIN_GUI_WIDTH, G_MIN_GUI_HEIGHT); refresh(); show(argc, argv); } /* -------------------------------------------------------------------------- */ gdMainWindow::~gdMainWindow() { m_conf.mainWindowX = x(); m_conf.mainWindowY = y(); m_conf.mainWindowW = w(); m_conf.mainWindowH = h(); } /* -------------------------------------------------------------------------- */ void gdMainWindow::refresh() { mainIO->refresh(); mainTimer->refresh(); mainTransport->refresh(); sequencer->refresh(); keyboard->refresh(); } /* -------------------------------------------------------------------------- */ void gdMainWindow::rebuild() { keyboard->rebuild(); mainIO->rebuild(); mainTimer->rebuild(); } /* -------------------------------------------------------------------------- */ void gdMainWindow::clearKeyboard() { keyboard->init(); } /* -------------------------------------------------------------------------- */ gdMainWindow::ScopedProgress gdMainWindow::getScopedProgress(const char* msg) { return {m_progress, msg}; } } // namespace giada::vgiada-0.22.0/src/gui/dialogs/mainWindow.h000066400000000000000000000042671425106661500201410ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_MAINWINDOW_H #define GD_MAINWINDOW_H #include "core/conf.h" #include "gui/dialogs/progress.h" #include "window.h" namespace giada::v { class geKeyboard; class geMainIO; class geMainMenu; class geSequencer; class geMainTransport; class geMainTimer; class gdMainWindow : public gdWindow { class ScopedProgress; public: gdMainWindow(int w, int h, const char* title, int argc, char** argv, m::Conf::Data&); ~gdMainWindow(); void refresh() override; void rebuild() override; /* clearKeyboard Resets Keyboard to initial state, with no columns. */ void clearKeyboard(); [[nodiscard]] ScopedProgress getScopedProgress(const char* msg); geKeyboard* keyboard; geSequencer* sequencer; geMainMenu* mainMenu; geMainIO* mainIO; geMainTimer* mainTimer; geMainTransport* mainTransport; private: class ScopedProgress { public: ScopedProgress(gdProgress&, const char* msg); ~ScopedProgress(); gdProgress& get(); private: gdProgress& m_progress; }; m::Conf::Data& m_conf; gdProgress m_progress; }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/midiIO/000077500000000000000000000000001425106661500170155ustar00rootroot00000000000000giada-0.22.0/src/gui/dialogs/midiIO/midiInputBase.cpp000066400000000000000000000036661425106661500222710ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "midiInputBase.h" #include "core/conf.h" #include "glue/io.h" namespace giada::v { gdMidiInputBase::gdMidiInputBase(int x, int y, int w, int h, const char* title, m::Conf::Data& c) : gdWindow(x, y, w, h, title) , m_conf(c) { } /* -------------------------------------------------------------------------- */ gdMidiInputBase::~gdMidiInputBase() { c::io::stopMidiLearn(); m_conf.midiInputX = x(); m_conf.midiInputY = y(); m_conf.midiInputW = w(); m_conf.midiInputH = h(); } /* -------------------------------------------------------------------------- */ void gdMidiInputBase::cb_close(Fl_Widget* /*w*/, void* p) { ((gdMidiInputBase*)p)->cb_close(); } /* -------------------------------------------------------------------------- */ void gdMidiInputBase::cb_close() { do_callback(); } } // namespace giada::vgiada-0.22.0/src/gui/dialogs/midiIO/midiInputBase.h000066400000000000000000000033101425106661500217200ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_MIDI_INPUT_BASE_H #define GD_MIDI_INPUT_BASE_H #include "core/conf.h" #include "gui/dialogs/window.h" #include "gui/elems/midiIO/midiLearner.h" class geCheck; namespace giada::v { class geButton; class geChoice; class gdMidiInputBase : public gdWindow { public: virtual ~gdMidiInputBase(); protected: gdMidiInputBase(int x, int y, int w, int h, const char* title, m::Conf::Data&); static void cb_close(Fl_Widget* /*w*/, void* p); void cb_close(); m::Conf::Data& m_conf; geButton* m_ok; geCheck* m_enable; geChoice* m_channel; }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/midiIO/midiInputChannel.cpp000066400000000000000000000220471425106661500227610ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/midiIO/midiInputChannel.h" #include "core/conf.h" #include "core/const.h" #include "gui/elems/basics/box.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 "gui/elems/basics/scrollPack.h" #include "gui/elems/midiIO/midiLearner.h" #include "gui/elems/midiIO/midiLearnerPack.h" #include "gui/ui.h" #include "utils/gui.h" #include "utils/log.h" #include "utils/string.h" #include #include #include #ifdef WITH_VST #include "core/plugins/plugin.h" #endif extern giada::v::Ui g_ui; namespace giada::v { geChannelLearnerPack::geChannelLearnerPack(int x, int y, const c::io::Channel_InputData& channel) : geMidiLearnerPack(x, y, g_ui.langMapper.get(LangMap::MIDIINPUT_CHANNEL_LEARN_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(g_ui.langMapper.get(LangMap::MIDIINPUT_CHANNEL_LEARN_KEYPRESS), G_MIDI_IN_KEYPRESS); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIINPUT_CHANNEL_LEARN_KEYREL), G_MIDI_IN_KEYREL); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIINPUT_CHANNEL_LEARN_KEYKILL), G_MIDI_IN_KILL); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIINPUT_CHANNEL_LEARN_ARM), G_MIDI_IN_ARM); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIINPUT_CHANNEL_LEARN_MUTE), G_MIDI_IN_MUTE); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIINPUT_CHANNEL_LEARN_SOLO), G_MIDI_IN_SOLO); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIINPUT_CHANNEL_LEARN_VOLUME), G_MIDI_IN_VOLUME); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIINPUT_CHANNEL_LEARN_PITCH), G_MIDI_IN_PITCH, /*visible=*/channel.channelType == ChannelType::SAMPLE); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIINPUT_CHANNEL_LEARN_READACTIONS), G_MIDI_IN_READ_ACTIONS, /*visible=*/channel.channelType == ChannelType::SAMPLE); } /* -------------------------------------------------------------------------- */ void geChannelLearnerPack::update(const c::io::Channel_InputData& d) { learners[0]->update(d.keyPress); learners[1]->update(d.keyRelease); learners[2]->update(d.kill); learners[3]->update(d.arm); learners[4]->update(d.mute); learners[5]->update(d.solo); learners[6]->update(d.volume); learners[7]->update(d.pitch); learners[8]->update(d.readActions); 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); }); 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; for (const c::io::PluginParamData& param : d.params) learners[i++]->update(param.value); setEnabled(enabled); } #endif /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ gdMidiInputChannel::gdMidiInputChannel(ID channelId, m::Conf::Data& c) : gdMidiInputBase(c.midiInputX, c.midiInputY, c.midiInputW, c.midiInputH, g_ui.langMapper.get(LangMap::MIDIINPUT_CHANNEL_TITLE), c) , 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, g_ui.langMapper.get(LangMap::MIDIINPUT_CHANNEL_ENABLE)); 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, g_ui.langMapper.get(LangMap::MIDIINPUT_CHANNEL_VELOCITYDRIVESVOL)); groupHeader->add(m_enable); groupHeader->add(m_channel); groupHeader->add(m_veloAsVol); groupHeader->resizable(nullptr); /* 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->add(new geChannelLearnerPack(0, 0, m_data)); #ifdef WITH_VST for (c::io::PluginData& plugin : m_data.plugins) m_container->add(new gePluginLearnerPack(0, 0, plugin)); #endif /* 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, g_ui.langMapper.get(LangMap::COMMON_CLOSE)); groupButtons->add(spacer); groupButtons->add(m_ok); groupButtons->resizable(spacer); m_ok->callback(cb_close, (void*)this); m_enable->callback(cb_enable, (void*)this); m_channel->addItem("Channel (any)"); m_channel->addItem("Channel 1"); m_channel->addItem("Channel 2"); m_channel->addItem("Channel 3"); m_channel->addItem("Channel 4"); m_channel->addItem("Channel 5"); m_channel->addItem("Channel 6"); m_channel->addItem("Channel 7"); m_channel->addItem("Channel 8"); m_channel->addItem("Channel 9"); m_channel->addItem("Channel 10"); m_channel->addItem("Channel 11"); m_channel->addItem("Channel 12"); m_channel->addItem("Channel 13"); m_channel->addItem("Channel 14"); m_channel->addItem("Channel 15"); m_channel->addItem("Channel 16"); m_channel->onChange = [this](ID id) { c::io::channel_setMidiInputFilter(m_data.channelId, id == 0 ? -1 : id - 1); }; m_veloAsVol->callback(cb_veloAsVol, (void*)this); add(groupHeader); add(m_container); add(groupButtons); resizable(m_container); u::gui::setFavicon(this); set_modal(); rebuild(); show(); } /* -------------------------------------------------------------------------- */ void gdMidiInputChannel::rebuild() { m_data = c::io::channel_getInputData(m_channelId); m_enable->value(m_data.enabled); if (m_data.channelType == ChannelType::SAMPLE) { m_veloAsVol->activate(); m_veloAsVol->value(m_data.velocityAsVol); } else m_veloAsVol->deactivate(); int i = 0; static_cast(m_container->getChild(i++))->update(m_data); #ifdef WITH_VST for (c::io::PluginData& plugin : m_data.plugins) static_cast(m_container->getChild(i++))->update(plugin, m_data.enabled); #endif m_channel->showItem(m_data.filter == -1 ? 0 : m_data.filter + 1); if (m_data.enabled) { m_channel->activate(); if (m_data.channelType == ChannelType::SAMPLE) m_veloAsVol->activate(); } else { m_channel->deactivate(); m_veloAsVol->deactivate(); } } /* -------------------------------------------------------------------------- */ void gdMidiInputChannel::cb_enable(Fl_Widget* /*w*/, void* p) { ((gdMidiInputChannel*)p)->cb_enable(); } 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()); } } // namespace giada::v giada-0.22.0/src/gui/dialogs/midiIO/midiInputChannel.h000066400000000000000000000046021425106661500224230ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * midiInputChannel * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_MIDI_INPUT_CHANNEL_H #define GD_MIDI_INPUT_CHANNEL_H #include "core/conf.h" #include "glue/io.h" #include "gui/elems/midiIO/midiLearnerPack.h" #include "midiInputBase.h" class geCheck; namespace giada::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, m::Conf::Data&); void rebuild() override; private: static void cb_enable(Fl_Widget* /*w*/, void* p); static void cb_veloAsVol(Fl_Widget* /*w*/, void* p); void cb_enable(); void cb_veloAsVol(); ID m_channelId; c::io::Channel_InputData m_data; geScrollPack* m_container; geCheck* m_veloAsVol; }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/midiIO/midiInputMaster.cpp000066400000000000000000000133711425106661500226440ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/midiIO/midiInputMaster.h" #include "core/conf.h" #include "core/const.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 "gui/elems/basics/scrollPack.h" #include "gui/elems/midiIO/midiLearner.h" #include "gui/ui.h" #include "utils/gui.h" extern giada::v::Ui g_ui; namespace giada::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(g_ui.langMapper.get(LangMap::MIDIINPUT_MASTER_LEARN_REWIND), G_MIDI_IN_REWIND); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIINPUT_MASTER_LEARN_PLAYSTOP), G_MIDI_IN_START_STOP); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIINPUT_MASTER_LEARN_ACTIONREC), G_MIDI_IN_ACTION_REC); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIINPUT_MASTER_LEARN_INPUTREC), G_MIDI_IN_INPUT_REC); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIINPUT_MASTER_LEARN_METRONOME), G_MIDI_IN_METRONOME); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIINPUT_MASTER_LEARN_INVOLUME), G_MIDI_IN_VOLUME_IN); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIINPUT_MASTER_LEARN_OUTVOLUME), G_MIDI_IN_VOLUME_OUT); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIINPUT_MASTER_LEARN_SEQDOUBLE), G_MIDI_IN_BEAT_DOUBLE); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIINPUT_MASTER_LEARN_SEQHALF), 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[5]->update(d.volumeIn); learners[6]->update(d.volumeOut); learners[7]->update(d.beatDouble); learners[8]->update(d.beatHalf); setEnabled(d.enabled); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ gdMidiInputMaster::gdMidiInputMaster(m::Conf::Data& c) : gdMidiInputBase(c.midiInputX, c.midiInputY, 300, 284, g_ui.langMapper.get(LangMap::MIDIINPUT_MASTER_TITLE), c) { end(); geGroup* groupHeader = new geGroup(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN); m_enable = new geCheck(0, 0, 120, G_GUI_UNIT, g_ui.langMapper.get(LangMap::MIDIINPUT_MASTER_ENABLE)); 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); m_learners = new geMasterLearnerPack(G_GUI_OUTER_MARGIN, groupHeader->y() + groupHeader->h() + G_GUI_OUTER_MARGIN); m_ok = new geButton(w() - 88, m_learners->y() + m_learners->h() + G_GUI_OUTER_MARGIN, 80, G_GUI_UNIT, g_ui.langMapper.get(LangMap::COMMON_CLOSE)); add(groupHeader); add(m_learners); add(m_ok); m_ok->callback(cb_close, (void*)this); m_enable->callback(cb_enable, (void*)this); m_channel->addItem("Channel (any)"); m_channel->addItem("Channel 1"); m_channel->addItem("Channel 2"); m_channel->addItem("Channel 3"); m_channel->addItem("Channel 4"); m_channel->addItem("Channel 5"); m_channel->addItem("Channel 6"); m_channel->addItem("Channel 7"); m_channel->addItem("Channel 8"); m_channel->addItem("Channel 9"); m_channel->addItem("Channel 10"); m_channel->addItem("Channel 11"); m_channel->addItem("Channel 12"); m_channel->addItem("Channel 13"); m_channel->addItem("Channel 14"); m_channel->addItem("Channel 15"); m_channel->addItem("Channel 16"); m_channel->onChange = [](ID id) { c::io::master_setMidiFilter(id == 0 ? -1 : id - 1); }; u::gui::setFavicon(this); set_modal(); rebuild(); show(); } /* -------------------------------------------------------------------------- */ void gdMidiInputMaster::rebuild() { m_data = c::io::master_getInputData(); m_enable->value(m_data.enabled); m_channel->showItem(m_data.filter - 1 ? 0 : m_data.filter + 1); m_learners->update(m_data); 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_enable() { c::io::master_enableMidiLearn(m_enable->value()); } } // namespace giada::vgiada-0.22.0/src/gui/dialogs/midiIO/midiInputMaster.h000066400000000000000000000036001425106661500223030ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_MIDI_INPUT_MASTER_H #define GD_MIDI_INPUT_MASTER_H #include "core/conf.h" #include "glue/io.h" #include "gui/elems/midiIO/midiLearnerPack.h" #include "midiInputBase.h" class geCheck; namespace giada::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(m::Conf::Data&); void rebuild() override; private: static void cb_enable(Fl_Widget* /*w*/, void* p); void cb_enable(); c::io::Master_InputData m_data; geMasterLearnerPack* m_learners; }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/midiIO/midiOutputBase.cpp000066400000000000000000000066311425106661500224650ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/midiIO/midiOutputBase.h" #include "glue/io.h" #include "gui/elems/basics/check.h" #include "gui/elems/midiIO/midiLearner.h" #include "gui/ui.h" extern giada::v::Ui g_ui; namespace giada::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); }); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIOUTPUT_CHANNEL_LEARN_PLAYING), G_MIDI_OUT_L_PLAYING); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIOUTPUT_CHANNEL_LEARN_MUTE), G_MIDI_OUT_L_MUTE); addMidiLearner(g_ui.langMapper.get(LangMap::MIDIOUTPUT_CHANNEL_LEARN_SOLO), G_MIDI_OUT_L_SOLO); } /* -------------------------------------------------------------------------- */ void geLightningLearnerPack::update(const c::io::Channel_OutputData& d) { learners[0]->update(d.lightningPlaying); learners[1]->update(d.lightningMute); learners[2]->update(d.lightningSolo); setEnabled(d.lightningEnabled); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ gdMidiOutputBase::gdMidiOutputBase(int w, int h, ID channelId) : gdWindow(w, h, g_ui.langMapper.get(LangMap::MIDIOUTPUT_CHANNEL_TITLE)) , m_channelId(channelId) { } /* -------------------------------------------------------------------------- */ gdMidiOutputBase::~gdMidiOutputBase() { c::io::stopMidiLearn(); } /* -------------------------------------------------------------------------- */ 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()); } } // namespace giada::v giada-0.22.0/src/gui/dialogs/midiIO/midiOutputBase.h000066400000000000000000000050141425106661500221240ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_MIDI_OUTPUT_BASE_H #define GD_MIDI_OUTPUT_BASE_H #include "core/types.h" #include "glue/io.h" #include "gui/dialogs/window.h" #include "gui/elems/midiIO/midiLearner.h" #include "gui/elems/midiIO/midiLearnerPack.h" 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 { class geButton; 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: /* cb_close close current window. */ static void cb_close(Fl_Widget* /*w*/, void* p); void cb_close(); static void cb_enableLightning(Fl_Widget* /*w*/, void* p); void cb_enableLightning(); ID m_channelId; c::io::Channel_OutputData m_data; geLightningLearnerPack* m_learners; geButton* m_close; geCheck* m_enableLightning; }; } // namespace v } // namespace giada #endif giada-0.22.0/src/gui/dialogs/midiIO/midiOutputMidiCh.cpp000066400000000000000000000101761425106661500227470ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/midiIO/midiOutputMidiCh.h" #include "glue/io.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 "gui/ui.h" #include "utils/gui.h" #include extern giada::v::Ui g_ui; namespace giada::v { gdMidiOutputMidiCh::gdMidiOutputMidiCh(ID channelId) : gdMidiOutputBase(300, 168, channelId) { end(); m_enableOut = new geCheck(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, 150, G_GUI_UNIT, g_ui.langMapper.get(LangMap::MIDIOUTPUT_CHANNEL_ENABLE)); 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, g_ui.langMapper.get(LangMap::MIDIOUTPUT_CHANNEL_ENABLE_LIGHTNING)); 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, g_ui.langMapper.get(LangMap::COMMON_CLOSE)); add(m_enableOut); add(m_chanListOut); add(m_enableLightning); add(m_learners); add(m_close); m_chanListOut->addItem("Channel 1"); m_chanListOut->addItem("Channel 2"); m_chanListOut->addItem("Channel 3"); m_chanListOut->addItem("Channel 4"); m_chanListOut->addItem("Channel 5"); m_chanListOut->addItem("Channel 6"); m_chanListOut->addItem("Channel 7"); m_chanListOut->addItem("Channel 8"); m_chanListOut->addItem("Channel 9"); m_chanListOut->addItem("Channel 10"); m_chanListOut->addItem("Channel 11"); m_chanListOut->addItem("Channel 12"); m_chanListOut->addItem("Channel 13"); m_chanListOut->addItem("Channel 14"); m_chanListOut->addItem("Channel 15"); m_chanListOut->addItem("Channel 16"); m_chanListOut->showItem(0); m_chanListOut->onChange = [this](ID id) { c::io::channel_setMidiOutputFilter(m_channelId, id); }; m_enableOut->callback(cb_enableOut, (void*)this); m_enableLightning->callback(cb_enableLightning, (void*)this); m_close->callback(cb_close, (void*)this); u::gui::setFavicon(this); set_modal(); rebuild(); 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->showItem(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() { c::io::channel_enableMidiOutput(m_channelId, m_enableOut->value()); } } // namespace giada::v giada-0.22.0/src/gui/dialogs/midiIO/midiOutputMidiCh.h000066400000000000000000000030771425106661500224160ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * midiOutputMidiCh * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_MIDI_OUTPUT_MIDI_CH_H #define GD_MIDI_OUTPUT_MIDI_CH_H #include "midiOutputBase.h" namespace giada::v { class geChoice; class gdMidiOutputMidiCh : public gdMidiOutputBase { public: gdMidiOutputMidiCh(ID channelId); void rebuild() override; private: static void cb_enableOut(Fl_Widget* /*w*/, void* p); void cb_enableOut(); geCheck* m_enableOut; geChoice* m_chanListOut; }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/midiIO/midiOutputSampleCh.cpp000066400000000000000000000046721425106661500233120ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/midiIO/midiOutputSampleCh.h" #include "core/model/model.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/check.h" #include "gui/elems/midiIO/midiLearner.h" #include "gui/ui.h" #include "utils/gui.h" #include extern giada::v::Ui g_ui; namespace giada::v { gdMidiOutputSampleCh::gdMidiOutputSampleCh(ID channelId) : gdMidiOutputBase(300, 140, channelId) { end(); m_enableLightning = new geCheck(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, 120, 20, g_ui.langMapper.get(LangMap::MIDIOUTPUT_CHANNEL_ENABLE_LIGHTNING)); 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, g_ui.langMapper.get(LangMap::COMMON_CLOSE)); add(m_enableLightning); add(m_learners); add(m_close); m_close->callback(cb_close, (void*)this); m_enableLightning->callback(cb_enableLightning, (void*)this); u::gui::setFavicon(this); set_modal(); rebuild(); show(); } /* -------------------------------------------------------------------------- */ void gdMidiOutputSampleCh::rebuild() { m_data = c::io::channel_getOutputData(m_channelId); m_enableLightning->value(m_data.lightningEnabled); m_learners->update(m_data); } } // namespace giada::v giada-0.22.0/src/gui/dialogs/midiIO/midiOutputSampleCh.h000066400000000000000000000026511425106661500227520ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_MIDI_OUTPUT_SAMPLE_CH_H #define GD_MIDI_OUTPUT_SAMPLE_CH_H #include "midiOutputBase.h" namespace giada { namespace v { class gdMidiOutputSampleCh : public gdMidiOutputBase { public: gdMidiOutputSampleCh(ID channelId); void rebuild() override; }; } // namespace v } // namespace giada #endif giada-0.22.0/src/gui/dialogs/missingAssets.cpp000066400000000000000000000060361425106661500212100ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/missingAssets.h" #include "core/engine.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/browser.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/flex.h" #include "gui/ui.h" #include "utils/gui.h" #include extern giada::v::Ui g_ui; namespace giada::v { gdMissingAssets::gdMissingAssets(const m::LoadState& state) : gdWindow(u::gui::getCenterWinBounds(400, 300), g_ui.langMapper.get(LangMap::COMMON_WARNING)) { geFlex* container = new geFlex(getContentBounds().reduced({G_GUI_OUTER_MARGIN}), Direction::VERTICAL, G_GUI_OUTER_MARGIN); { geFlex* body = new geFlex(Direction::VERTICAL, G_GUI_INNER_MARGIN); { geBox* textIntro = new geBox(g_ui.langMapper.get(LangMap::MISSINGASSETS_INTRO), FL_ALIGN_LEFT); textIntro->color(G_COLOR_BLUE); body->add(textIntro, G_GUI_UNIT); if (state.missingWaves.size() > 0) { geBrowser* waves = new geBrowser(); for (const std::string& s : state.missingWaves) waves->add(s.c_str()); body->add(new geBox(g_ui.langMapper.get(LangMap::MISSINGASSETS_AUDIOFILES), FL_ALIGN_LEFT), G_GUI_UNIT); body->add(waves); } if (state.missingPlugins.size() > 0) { geBrowser* plugins = new geBrowser(); for (const std::string& s : state.missingPlugins) plugins->add(s.c_str()); body->add(new geBox(g_ui.langMapper.get(LangMap::MISSINGASSETS_PLUGINS), FL_ALIGN_LEFT), G_GUI_UNIT); body->add(plugins); } body->end(); } geFlex* footer = new geFlex(Direction::HORIZONTAL); { geButton* close = new geButton(g_ui.langMapper.get(LangMap::COMMON_CLOSE)); close->onClick = [this]() { do_callback(); }; footer->add(new geBox()); // Spacer footer->add(close, 80); footer->end(); } container->add(body); container->add(footer, G_GUI_UNIT); container->end(); } add(container); resizable(container); set_modal(); u::gui::setFavicon(this); show(); } } // namespace giada::vgiada-0.22.0/src/gui/dialogs/missingAssets.h000066400000000000000000000026121425106661500206510ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_MISSING_ASSETS_H #define GD_MISSING_ASSETS_H #include "gui/dialogs/window.h" namespace giada::m { struct LoadState; } namespace giada::v { class gdMissingAssets : public gdWindow { public: gdMissingAssets(const m::LoadState&); }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/pluginChooser.cpp000066400000000000000000000104131425106661500211670ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #include "pluginChooser.h" #include "core/conf.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/flex.h" #include "gui/elems/plugin/pluginBrowser.h" #include "gui/ui.h" #include "utils/gui.h" extern giada::v::Ui g_ui; namespace giada::v { gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, ID channelId, m::Conf::Data& c) : gdWindow(X, Y, W, H, g_ui.langMapper.get(LangMap::PLUGINCHOOSER_TITLE)) , m_conf(c) , m_channelId(channelId) { geFlex* container = new geFlex(getContentBounds().reduced({G_GUI_OUTER_MARGIN}), Direction::VERTICAL, G_GUI_OUTER_MARGIN); { geFlex* header = new geFlex(Direction::HORIZONTAL, G_GUI_INNER_MARGIN); { sortMethod = new geChoice(g_ui.langMapper.get(LangMap::PLUGINCHOOSER_SORTBY), 0); header->add(sortMethod, 180); header->add(new geBox()); header->end(); } browser = new v::gePluginBrowser(0, 0, 0, 0); geFlex* footer = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN); { addBtn = new geButton(g_ui.langMapper.get(LangMap::COMMON_ADD)); cancelBtn = new geButton(g_ui.langMapper.get(LangMap::COMMON_CANCEL)); footer->add(new geBox()); footer->add(cancelBtn, 80); footer->add(addBtn, 80); footer->end(); } container->add(header, G_GUI_UNIT); container->add(browser); container->add(footer, G_GUI_UNIT); container->end(); } add(container); resizable(container); sortMethod->addItem(g_ui.langMapper.get(LangMap::PLUGINCHOOSER_SORTBY_NAME)); sortMethod->addItem(g_ui.langMapper.get(LangMap::PLUGINCHOOSER_SORTBY_CATEGORY)); sortMethod->addItem(g_ui.langMapper.get(LangMap::PLUGINCHOOSER_SORTBY_MANIFACTURER)); sortMethod->addItem(g_ui.langMapper.get(LangMap::PLUGINCHOOSER_SORTBY_FORMAT)); sortMethod->showItem(m_conf.pluginSortMethod); sortMethod->onChange = [this](ID id) { c::plugin::sortPlugins(static_cast(id)); browser->refresh(); }; addBtn->callback(cb_add, (void*)this); addBtn->shortcut(FL_Enter); cancelBtn->callback(cb_close, (void*)this); u::gui::setFavicon(this); show(); } /* -------------------------------------------------------------------------- */ gdPluginChooser::~gdPluginChooser() { m_conf.pluginChooserX = x(); m_conf.pluginChooserY = y(); m_conf.pluginChooserW = w(); m_conf.pluginChooserH = h(); m_conf.pluginSortMethod = sortMethod->getSelectedId(); } /* -------------------------------------------------------------------------- */ 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_close() { do_callback(); } /* -------------------------------------------------------------------------- */ void gdPluginChooser::cb_add() { int pluginIndex = browser->value() - 3; // subtract header lines if (pluginIndex < 0) return; c::plugin::addPlugin(pluginIndex, m_channelId); do_callback(); } } // namespace giada::v #endif // #ifdef WITH_VST giada-0.22.0/src/gui/dialogs/pluginChooser.h000066400000000000000000000036061425106661500206420ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #ifndef GD_PLUGIN_CHOOSER_H #define GD_PLUGIN_CHOOSER_H #include "core/conf.h" #include "core/types.h" #include "window.h" #include #include namespace giada::v { class geButton; class geChoice; class gePluginBrowser; class gdPluginChooser : public gdWindow { public: gdPluginChooser(int x, int y, int w, int h, ID channelId, m::Conf::Data&); ~gdPluginChooser(); private: static void cb_close(Fl_Widget* /*w*/, void* p); static void cb_add(Fl_Widget* /*w*/, void* p); void cb_close(); void cb_add(); m::Conf::Data& m_conf; geChoice* sortMethod; geButton* addBtn; geButton* cancelBtn; gePluginBrowser* browser; ID m_channelId; }; } // namespace giada::v #endif #endif // #ifdef WITH_VST giada-0.22.0/src/gui/dialogs/pluginList.cpp000066400000000000000000000101621425106661500205010ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #include "gui/dialogs/pluginList.h" #include "core/conf.h" #include "core/const.h" #include "glue/layout.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/keyboard/channel.h" #include "gui/elems/mainWindow/mainIO.h" #include "gui/elems/plugin/pluginElement.h" #include "gui/ui.h" #include "utils/gui.h" #include "utils/string.h" #include #include extern giada::v::Ui g_ui; namespace giada::v { gdPluginList::gdPluginList(ID channelId, m::Conf::Data& c) : gdWindow(c.pluginListX, c.pluginListY, 468, 204) , m_conf(c) , 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), Direction::VERTICAL); list->end(); add(list); resizable(list); u::gui::setFavicon(this); set_non_modal(); rebuild(); show(); } /* -------------------------------------------------------------------------- */ gdPluginList::~gdPluginList() { m_conf.pluginListX = x(); m_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(g_ui.langMapper.get(LangMap::PLUGINLIST_TITLE_MASTEROUT)); else if (m_plugins.channelId == m::Mixer::MASTER_IN_CHANNEL_ID) label(g_ui.langMapper.get(LangMap::PLUGINLIST_TITLE_MASTERIN)); else label(g_ui.langMapper.get(LangMap::PLUGINLIST_TITLE_CHANNEL)); /* Clear the previous list. */ list->clear(); list->scroll_to(0, 0); 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, g_ui.langMapper.get(LangMap::PLUGINLIST_ADDPLUGIN))); addPlugin->callback(cb_addPlugin, (void*)this); } /* -------------------------------------------------------------------------- */ void gdPluginList::cb_addPlugin() { c::layout::openPluginChooser(m_plugins.channelId); } /* -------------------------------------------------------------------------- */ const gePluginElement& gdPluginList::getNextElement(const gePluginElement& currEl) const { int curr = list->find(currEl); int next = curr + 1; if (next > list->countChildren() - 2) next = list->countChildren() - 2; return *static_cast(list->child(next)); } const gePluginElement& gdPluginList::getPrevElement(const gePluginElement& currEl) const { int curr = list->find(currEl); int prev = curr - 1; if (prev < 0) prev = 0; return *static_cast(list->child(prev)); } } // namespace giada::v #endif // #ifdef WITH_VST giada-0.22.0/src/gui/dialogs/pluginList.h000066400000000000000000000036271425106661500201560ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #ifndef GD_PLUGINLIST_H #define GD_PLUGINLIST_H #include "core/conf.h" #include "glue/plugin.h" #include "window.h" namespace giada::v { class geButton; class geLiquidScroll; class gePluginElement; class gdPluginList : public gdWindow { public: gdPluginList(ID channelId, m::Conf::Data&); ~gdPluginList(); void rebuild() override; const gePluginElement& getNextElement(const gePluginElement& curr) const; const gePluginElement& getPrevElement(const gePluginElement& curr) const; private: static void cb_addPlugin(Fl_Widget* /*w*/, void* p); void cb_addPlugin(); m::Conf::Data& m_conf; geButton* addPlugin; geLiquidScroll* list; ID m_channelId; c::plugin::Plugins m_plugins; }; } // namespace giada::v #endif #endif // #ifdef WITH_VST giada-0.22.0/src/gui/dialogs/pluginWindow.cpp000066400000000000000000000051711425106661500210410ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #include "pluginWindow.h" #include "core/const.h" #include "glue/plugin.h" #include "gui/elems/basics/liquidScroll.h" #include "gui/elems/plugin/pluginParameter.h" #include "utils/gui.h" #include namespace giada::v { gdPluginWindow::gdPluginWindow(const c::plugin::Plugin& plugin) : gdWindow(450, 156) , m_plugin(plugin) { set_non_modal(); begin(); m_list = new geLiquidScroll(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w() - (G_GUI_OUTER_MARGIN * 2), h() - (G_GUI_OUTER_MARGIN * 2), Direction::VERTICAL); 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.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))); resizable(m_list); u::gui::setFavicon(this); show(); } /* -------------------------------------------------------------------------- */ void gdPluginWindow::updateParameters(bool changeSlider) { for (int index : m_plugin.paramIndexes) static_cast(m_list->child(index))->update(c::plugin::getParam(index, m_plugin.getPluginRef(), m_plugin.channelId), changeSlider); } } // namespace giada::v #endif // #ifdef WITH_VST giada-0.22.0/src/gui/dialogs/pluginWindow.h000066400000000000000000000032061425106661500205030ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #ifndef GD_PLUGIN_WINDOW_H #define GD_PLUGIN_WINDOW_H #include "window.h" class geSlider; namespace giada::c::plugin { struct Plugin; } namespace giada::m { class Plugin; } namespace giada::v { class geBox; class geLiquidScroll; class gdPluginWindow : public gdWindow { public: gdPluginWindow(const c::plugin::Plugin&); void updateParameters(bool changeSlider = false); private: const c::plugin::Plugin& m_plugin; geLiquidScroll* m_list; }; } // namespace giada::v #endif #endif // #ifdef WITH_VST giada-0.22.0/src/gui/dialogs/pluginWindowGUI.cpp000066400000000000000000000062201425106661500214020ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #include "gui/dialogs/pluginWindowGUI.h" #include "core/const.h" #include "glue/plugin.h" #include "utils/gui.h" #include "utils/log.h" #include #ifdef G_OS_MAC #import "utils/cocoa.h" // objective-c #endif namespace giada::v { gdPluginWindowGUI::gdPluginWindowGUI(c::plugin::Plugin& p) #ifdef G_OS_MAC : gdWindow(Fl::w(), Fl::h()) #else : gdWindow(320, 200) #endif , m_plugin(p) { /* Make sure to wait_for_expose() before opening the editor: the window must be exposed and visible first. Don't fuck with multithreading! */ copy_label(m_plugin.name.c_str()); show(); wait_for_expose(); openEditor(); Fl::flush(); } /* -------------------------------------------------------------------------- */ gdPluginWindowGUI::~gdPluginWindowGUI() { c::plugin::stopDispatchLoop(); closeEditor(); u::log::print("[gdPluginWindowGUI::__cb_close] GUI closed, this=%p\n", (void*)this); } /* -------------------------------------------------------------------------- */ void gdPluginWindowGUI::openEditor() { u::log::print("[gdPluginWindowGUI] Opening editor, this=%p, xid=%p\n", this, reinterpret_cast(fl_xid(this))); m_editor.reset(m_plugin.createEditor()); if (m_editor == nullptr) { u::log::print("[gdPluginWindowGUI::openEditor] unable to create editor!\n"); return; } m_editor->setOpaque(true); #ifdef G_OS_MAC void* cocoaWindow = (void*)fl_xid(this); m_editor->addToDesktop(0, cocoa_getViewFromWindow(cocoaWindow)); #else m_editor->addToDesktop(0, reinterpret_cast(fl_xid(this))); #endif const int pluginW = m_editor->getWidth(); const int pluginH = m_editor->getHeight(); resize((Fl::w() - pluginW) / 2, (Fl::h() - pluginH) / 2, pluginW, pluginH); m_plugin.setResizeCallback([this](int w, int h) { resize(x(), y(), w, h); }); c::plugin::startDispatchLoop(); } /* -------------------------------------------------------------------------- */ void gdPluginWindowGUI::closeEditor() { m_plugin.setResizeCallback(nullptr); m_editor.reset(); } } // namespace giada::v #endif // #ifdef WITH_VST giada-0.22.0/src/gui/dialogs/pluginWindowGUI.h000066400000000000000000000033501425106661500210500ustar00rootroot00000000000000 /* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * gd_pluginWindowGUI * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #ifndef GD_PLUGIN_WINDOW_GUI_H #define GD_PLUGIN_WINDOW_GUI_H #include "deps/juce-config.h" #include "window.h" #include #include #include namespace giada::c::plugin { struct Plugin; } namespace giada::v { class gdPluginWindowGUI : public gdWindow { public: gdPluginWindowGUI(c::plugin::Plugin&); ~gdPluginWindowGUI(); void openEditor(); void closeEditor(); private: c::plugin::Plugin& m_plugin; std::unique_ptr m_editor; }; } // namespace giada::v #endif #endif // #ifdef WITH_VST giada-0.22.0/src/gui/dialogs/progress.cpp000066400000000000000000000043271425106661500202210ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/progress.h" #include "core/const.h" #include "deps/geompp/src/rect.hpp" #include "utils/gui.h" #include namespace giada::v { gdProgress::gdProgress() : gdWindow(300, 58) , m_text(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w() - (G_GUI_OUTER_MARGIN * 2), 30, "", FL_ALIGN_CENTER) , m_progress(G_GUI_OUTER_MARGIN, 40, w() - (G_GUI_OUTER_MARGIN * 2), 10) { end(); add(m_text); add(m_progress); m_progress.minimum(0.0f); m_progress.maximum(1.0f); m_progress.value(0.0f); hide(); border(0); set_modal(); } /* -------------------------------------------------------------------------- */ void gdProgress::setProgress(float p) { m_progress.value(p); redraw(); Fl::flush(); } /* -------------------------------------------------------------------------- */ void gdProgress::popup(const char* s) { m_text.copy_label(s); const int px = u::gui::centerWindowX(w()); const int py = u::gui::centerWindowY(h()); position(px, py); show(); wait_for_expose(); // No async bullshit, show it right away Fl::flush(); // Make sure everything is displayed } } // namespace giada::v giada-0.22.0/src/gui/dialogs/progress.h000066400000000000000000000027571425106661500176730ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_PROGRESS_H #define GD_PROGRESS_H #include "gui/dialogs/window.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/progress.h" namespace giada::v { class gdProgress : public gdWindow { public: gdProgress(); void setProgress(float p); void popup(const char* s); private: geBox m_text; geProgress m_progress; }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/sampleEditor.cpp000066400000000000000000000235141425106661500210040ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "glue/sampleEditor.h" #include "core/conf.h" #include "core/const.h" #include "core/graphics.h" #include "core/mixer.h" #include "core/wave.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/check.h" #include "gui/elems/basics/choice.h" #include "gui/elems/basics/dial.h" #include "gui/elems/basics/group.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/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/ui.h" #include "sampleEditor.h" #include "utils/gui.h" #include "utils/string.h" #include #include #include #include #include #ifdef G_OS_WINDOWS #undef IN #undef OUT #endif extern giada::v::Ui g_ui; namespace giada::v { gdSampleEditor::gdSampleEditor(ID channelId, m::Conf::Data& c) : gdWindow(c.sampleEditorX, c.sampleEditorY, c.sampleEditorW, c.sampleEditorH, g_ui.langMapper.get(LangMap::SAMPLEEDITOR_TITLE)) , m_channelId(channelId) , m_conf(c) { end(); gePack* upperBar = createUpperBar(); waveTools = new geWaveTools(G_GUI_OUTER_MARGIN, upperBar->y() + upperBar->h() + G_GUI_OUTER_MARGIN, w() - 16, h() - 168, m_conf.sampleEditorGridOn, m_conf.sampleEditorGridVal); 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); add(bottomBar); resizable(waveTools); u::gui::setFavicon(this); size_range(720, 480); set_non_modal(); rebuild(); show(); } /* -------------------------------------------------------------------------- */ gdSampleEditor::~gdSampleEditor() { m_conf.sampleEditorX = x(); m_conf.sampleEditorY = y(); m_conf.sampleEditorW = w(); m_conf.sampleEditorH = h(); m_conf.sampleEditorGridVal = grid->getSelectedId(); m_conf.sampleEditorGridOn = snap->value(); c::sampleEditor::stopPreview(); c::sampleEditor::cleanupPreview(); } /* -------------------------------------------------------------------------- */ void gdSampleEditor::rebuild() { m_data = c::sampleEditor::getData(m_channelId); waveTools->rebuild(m_data); volumeTool->rebuild(m_data); panTool->rebuild(m_data); pitchTool->rebuild(m_data); rangeTool->rebuild(m_data); shiftTool->rebuild(m_data); updateInfo(); if (m_data.isLogical) // Logical samples (aka takes) cannot be reloaded. 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, g_ui.langMapper.get(LangMap::SAMPLEEDITOR_RELOAD)); grid = new geChoice(0, 0, 50, G_GUI_UNIT); snap = new geCheck(0, 0, 12, G_GUI_UNIT, g_ui.langMapper.get(LangMap::COMMON_SNAPTOGRID)); 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); reload->callback(cb_reload, (void*)this); grid->addItem("1"); grid->addItem("2"); grid->addItem("3"); grid->addItem("4"); grid->addItem("6"); grid->addItem("8"); grid->addItem("16"); grid->addItem("32"); grid->addItem("64"); grid->copy_tooltip(g_ui.langMapper.get(LangMap::COMMON_GRIDRES)); grid->showItem(m_conf.sampleEditorGridVal); grid->onChange = [this](ID) { waveTools->waveform->setGridLevel(std::stoi(grid->getSelectedLabel())); }; snap->value(m_conf.sampleEditorGridOn); snap->copy_tooltip(g_ui.langMapper.get(LangMap::COMMON_SNAPTOGRID)); snap->callback(cb_enableSnap, (void*)this); /* TODO - redraw grid if != (off) */ zoomOut->callback(cb_zoomOut, (void*)this); zoomOut->copy_tooltip(g_ui.langMapper.get(LangMap::COMMON_ZOOMOUT)); zoomIn->callback(cb_zoomIn, (void*)this); zoomIn->copy_tooltip(g_ui.langMapper.get(LangMap::COMMON_ZOOMIN)); gePack* g = new gePack(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, Direction::HORIZONTAL); g->add(reload); g->add(grid); g->add(snap); g->add(sep1); g->add(zoomOut); 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); gePack* g = new gePack(x, y, Direction::VERTICAL); g->add(volumeTool); g->add(panTool); g->add(pitchTool); g->add(rangeTool); g->add(shiftTool); 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); play = new geStatusButton(rewind->x() + rewind->w() + 4, rewind->y(), 25, 25, play_xpm, pause_xpm); loop = new geCheck(play->x() + play->w() + 4, play->y(), 50, 25, g_ui.langMapper.get(LangMap::SAMPLEEDITOR_LOOP)); play->callback(cb_togglePreview, (void*)this); rewind->callback(cb_rewindPreview, (void*)this); geGroup* g = new geGroup(x, y); g->add(rewind); g->add(play); g->add(loop); 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); divisor1->box(FL_BORDER_BOX); divisor2->box(FL_BORDER_BOX); info->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_TOP); gePack* g = new gePack(x, y, Direction::HORIZONTAL, /*gutter=*/G_GUI_OUTER_MARGIN); g->add(previewBox); g->add(divisor1); g->add(opTools); g->add(divisor2); g->add(info); g->resizable(0); 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_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::playPreview(loop->value()); else c::sampleEditor::stopPreview(); } void gdSampleEditor::cb_rewindPreview() { c::sampleEditor::setPreviewTracker(m_data.begin); } /* -------------------------------------------------------------------------- */ void gdSampleEditor::cb_reload() { 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::updateInfo() { std::string infoText = fmt::format(g_ui.langMapper.get(LangMap::SAMPLEEDITOR_INFO), m_data.wavePath, m_data.waveSize, m_data.waveDuration, m_data.waveBits != 0 ? std::to_string(m_data.waveBits) : "?", m_data.waveRate); info->copy_label(infoText.c_str()); } } // namespace giada::v giada-0.22.0/src/gui/dialogs/sampleEditor.h000066400000000000000000000056111425106661500204470ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_EDITOR_H #define GD_EDITOR_H #include "core/conf.h" #include "core/types.h" #include "glue/sampleEditor.h" #include "window.h" class geCheck; class geStatusButton; namespace giada::m { class Wave; } namespace giada::v { class geBox; class geButton; class geChoice; class gePack; class geGroup; class geVolumeTool; class geWaveTools; class geBoostTool; class gePanTool; class gePitchTool; class geRangeTool; class geShiftTool; class gdSampleEditor : public gdWindow { friend class geWaveform; public: gdSampleEditor(ID channelId, m::Conf::Data&); ~gdSampleEditor(); void rebuild() override; void refresh() override; geChoice* grid; geCheck* snap; geBox* sep1; geButton* zoomIn; geButton* zoomOut; geWaveTools* waveTools; geVolumeTool* volumeTool; gePanTool* panTool; gePitchTool* pitchTool; geRangeTool* rangeTool; geShiftTool* shiftTool; geButton* reload; geStatusButton* play; geButton* rewind; geCheck* loop; geBox* info; private: gePack* createUpperBar(); gePack* createBottomBar(int x, int y, int h); geGroup* createPreviewBox(int x, int y, int h); 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_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_enableSnap(); void cb_togglePreview(); void cb_rewindPreview(); void updateInfo(); ID m_channelId; c::sampleEditor::Data m_data; m::Conf::Data& m_conf; }; } // namespace giada::v #endif giada-0.22.0/src/gui/dialogs/warnings.cpp000066400000000000000000000055601425106661500202050ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "warnings.h" #include "core/const.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/button.h" #include "gui/ui.h" #include "utils/gui.h" #include "window.h" #include #include extern giada::v::Ui g_ui; namespace giada::v { namespace { bool confirmRet_ = false; } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ void gdAlert(const char* msg) { gdWindow win(u::gui::getCenterWinBounds(300, 90), g_ui.langMapper.get(LangMap::COMMON_WARNING)); win.set_modal(); win.begin(); geBox* box = new geBox(10, 10, 280, 40, msg); geButton* b = new geButton(210, 60, 80, 20, g_ui.langMapper.get(LangMap::COMMON_CLOSE)); win.end(); box->labelsize(G_GUI_FONT_SIZE_BASE); b->shortcut(FL_Enter); b->onClick = [&win]() { win.hide(); }; u::gui::setFavicon(&win); win.show(); while (win.shown()) Fl::wait(); } /* -------------------------------------------------------------------------- */ int gdConfirmWin(const char* title, const char* msg) { gdWindow win(u::gui::getCenterWinBounds(300, 90), title); win.set_modal(); win.begin(); new geBox(10, 10, 280, 40, msg); geButton* ok = new geButton(212, 62, 80, 20, g_ui.langMapper.get(LangMap::COMMON_OK)); geButton* ko = new geButton(124, 62, 80, 20, g_ui.langMapper.get(LangMap::COMMON_CANCEL)); win.end(); ok->shortcut(FL_Enter); ok->onClick = [&win]() { confirmRet_ = true; win.hide(); }; ko->onClick = [&win]() { confirmRet_ = false; win.hide(); }; u::gui::setFavicon(&win); win.show(); while (win.shown()) Fl::wait(); return confirmRet_; } } // namespace giada::vgiada-0.22.0/src/gui/dialogs/warnings.h000066400000000000000000000024441425106661500176500ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_WARNINGS_H #define GD_WARNINGS_H namespace giada::v { void gdAlert(const char* c); int gdConfirmWin(const char* title, const char* msg); } // namespace v #endif giada-0.22.0/src/gui/dialogs/window.cpp000066400000000000000000000115271425106661500176640ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "window.h" #include "utils/log.h" #include namespace giada::v { 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) { end(); } /* -------------------------------------------------------------------------- */ gdWindow::gdWindow(int w, int h, const char* title, int id) : Fl_Double_Window(w, h, title) , id(id) , parent(nullptr) { end(); } /* -------------------------------------------------------------------------- */ gdWindow::gdWindow(geompp::Rect r, const char* title, int id) : gdWindow(r.x, r.y, r.w, r.h, title, id) { } /* -------------------------------------------------------------------------- */ gdWindow::~gdWindow() { /* delete all subwindows in order to empty the stack */ for (unsigned j = 0; j < subWindows.size(); j++) delete subWindows.at(j); subWindows.clear(); } /* -------------------------------------------------------------------------- */ /* this is the default callback of each window, fired when the user closes * the window with the 'x'. Watch out: is the parent that calls delSubWIndow */ void gdWindow::cb_closeChild(Fl_Widget* w, void* /*p*/) { /* Disable default FLTK behavior where 'escape' closes the window. */ if (Fl::event() == FL_SHORTCUT && Fl::event_key() == FL_Escape) return; gdWindow* child = (gdWindow*)w; if (child->getParent() != nullptr) (child->getParent())->delSubWindow(child); } /* -------------------------------------------------------------------------- */ void gdWindow::addSubWindow(gdWindow* w) { w->setParent(this); w->callback(cb_closeChild); // you can pass params: w->callback(cb_closeChild, (void*)params) subWindows.push_back(w); //debug(); } /* -------------------------------------------------------------------------- */ void gdWindow::delSubWindow(gdWindow* w) { for (unsigned j = 0; j < subWindows.size(); j++) 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) { 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 u::log::print("---- window stack (id=%d): ----\n", getId()); for (unsigned i=0; igetId()); u::log::print("----\n"); */ } /* -------------------------------------------------------------------------- */ geompp::Rect gdWindow::getContentBounds() const { return {0, 0, w(), h()}; } /* -------------------------------------------------------------------------- */ 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++) if (wid == subWindows.at(j)->getId()) return true; return false; } /* -------------------------------------------------------------------------- */ gdWindow* gdWindow::getChild(int wid) { for (unsigned j = 0; j < subWindows.size(); j++) if (wid == subWindows.at(j)->getId()) return subWindows.at(j); return nullptr; } } // namespace giada::v giada-0.22.0/src/gui/dialogs/window.h000066400000000000000000000046031425106661500173260ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GD_WINDOW_H #define GD_WINDOW_H #include "deps/geompp/src/rect.hpp" #include #include namespace giada::v { 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(geompp::Rect, const char* title = 0, int id = 0); ~gdWindow(); static void cb_closeChild(Fl_Widget* /*w*/, void* p); /* rebuild, refresh Rebuild() is called by the View Updater when something structural changes (e.g. a new channel added). Refresh() is called periodically by the View Updater during the refresh loop. */ virtual void rebuild(){}; virtual void refresh(){}; /* hasWindow True if the window with id 'id' exists in the stack. */ bool hasWindow(int id) const; int getId() const; void debug() const; geompp::Rect getContentBounds() const; 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: std::vector subWindows; int id; gdWindow* parent; }; } // namespace giada::v #endif giada-0.22.0/src/gui/dispatcher.cpp000066400000000000000000000075101425106661500170560ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "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 "gui/ui.h" #include #include extern giada::v::Ui g_ui; namespace giada::v { Dispatcher::Dispatcher(const m::Conf::KeyBindings& k) : m_keyBindings(k) , m_keyPressed(-1) { } /* -------------------------------------------------------------------------- */ void Dispatcher::perform(ID channelId, int event) const { if (event == FL_KEYDOWN) { if (Fl::event_ctrl()) c::events::toggleMuteChannel(channelId, Thread::MAIN); 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) 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 Dispatcher::dispatchChannels(int event) const { g_ui.mainWindow->keyboard->forEachChannel([=](geChannel& c) { if (c.handleKey(event)) perform(c.getData().id, event); }); } /* -------------------------------------------------------------------------- */ void Dispatcher::dispatchKey(int event) { assert(onEventOccured != nullptr); /* These events come from the keyboard, not from a direct interaction on the UI with the mouse/touch. */ if (event == FL_KEYDOWN) { if (m_keyPressed == Fl::event_key()) // Avoid key retrig return; m_keyPressed = Fl::event_key(); if (m_keyPressed == m_keyBindings.at(m::Conf::KEY_BIND_PLAY)) c::events::toggleSequencer(Thread::MAIN); else if (m_keyPressed == m_keyBindings.at(m::Conf::KEY_BIND_REWIND)) c::events::rewindSequencer(Thread::MAIN); else if (m_keyPressed == m_keyBindings.at(m::Conf::KEY_BIND_RECORD_ACTIONS)) c::events::toggleActionRecording(); else if (m_keyPressed == m_keyBindings.at(m::Conf::KEY_BIND_RECORD_INPUT)) c::events::toggleInputRecording(); else if (m_keyPressed == m_keyBindings.at(m::Conf::KEY_BIND_EXIT)) { c::events::stopActionRecording(); c::events::stopInputRecording(); } else { onEventOccured(); dispatchChannels(event); } } else if (event == FL_KEYUP) { m_keyPressed = -1; dispatchChannels(event); } } /* -------------------------------------------------------------------------- */ void Dispatcher::dispatchTouch(const geChannel& gch, bool status) { assert(onEventOccured != nullptr); onEventOccured(); perform(gch.getData().id, status ? FL_KEYDOWN : FL_KEYUP); } } // namespace giada::vgiada-0.22.0/src/gui/dispatcher.h000066400000000000000000000040751425106661500165260ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_V_DISPATCHER_H #define G_V_DISPATCHER_H #include "core/conf.h" #include "core/types.h" #include namespace giada::v { class geChannel; class Dispatcher final { public: Dispatcher(const m::Conf::KeyBindings& m_keyBindings); /* dispatchKey Processes a key pressed on the physical keyboard. */ void dispatchKey(int event); /* dispatchTouch Processes a mouse click/touch event. */ void dispatchTouch(const geChannel& gch, bool status); /* onEventOccured Callback fired when a key has been pressed or a mouse button clicked. */ std::function onEventOccured; private: void perform(ID channelId, int event) const; /* dispatchChannels Walks 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) const; const m::Conf::KeyBindings& m_keyBindings; int m_keyPressed; }; } // namespace giada::v #endifgiada-0.22.0/src/gui/drawing.cpp000066400000000000000000000037071425106661500163670ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "drawing.h" #include "utils/gui.h" #include #include namespace giada::v { void drawRectf(geompp::Rect r, Fl_Color c) { fl_rectf(r.x, r.y, r.w, r.h, c); } /* -------------------------------------------------------------------------- */ void drawRect(geompp::Rect r, Fl_Color c) { fl_rect(r.x, r.y, r.w, r.h, c); } /* -------------------------------------------------------------------------- */ void drawLine(geompp::Line l, Fl_Color c) { fl_color(c); fl_line(l.x1, l.y1, l.x2, l.y2); } /* -------------------------------------------------------------------------- */ void drawText(const std::string& s, geompp::Rect b, Fl_Color c, int alignment) { assert(!s.empty()); fl_color(c); fl_draw(u::gui::truncate(s, b.w - 16).c_str(), b.x, b.y, b.w, b.h, alignment); } } // namespace giada::v giada-0.22.0/src/gui/drawing.h000066400000000000000000000030151425106661500160240ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_V_DRAWING_H #define G_V_DRAWING_H #include "deps/geompp/src/rect.hpp" #include #include namespace giada::v { void drawRectf(geompp::Rect, Fl_Color); void drawRect(geompp::Rect, Fl_Color); void drawLine(geompp::Line, Fl_Color); void drawText(const std::string&, geompp::Rect, Fl_Color c, int alignment = FL_ALIGN_CENTER); } // namespace giada::v #endifgiada-0.22.0/src/gui/elems/000077500000000000000000000000001425106661500153265ustar00rootroot00000000000000giada-0.22.0/src/gui/elems/actionEditor/000077500000000000000000000000001425106661500177525ustar00rootroot00000000000000giada-0.22.0/src/gui/elems/actionEditor/baseAction.cpp000066400000000000000000000061161425106661500225320ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "baseAction.h" #include #include 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) , onRightEdge(false) , 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); } return 1; } default: return Fl_Widget::handle(e); } } /* -------------------------------------------------------------------------- */ void geBaseAction::setLeftEdge(Pixel p) { resize(p, y(), x() - p + w(), h()); if (w() < MIN_WIDTH) size(MIN_WIDTH, h()); } /* -------------------------------------------------------------------------- */ void geBaseAction::setRightEdge(Pixel p) { size(p, h()); if (w() < MIN_WIDTH) size(MIN_WIDTH, h()); } /* -------------------------------------------------------------------------- */ void geBaseAction::setPosition(Pixel p) { position(p, y()); } /* -------------------------------------------------------------------------- */ bool geBaseAction::isOnEdges() const { return onLeftEdge || onRightEdge; } } // namespace v } // namespace giadagiada-0.22.0/src/gui/elems/actionEditor/baseAction.h000066400000000000000000000036621425106661500222020ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_BASE_ACTION_H #define GE_BASE_ACTION_H #include "core/types.h" #include "src/core/actions/actions.h" #include namespace giada::m { struct Action; } 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); int handle(int e) override; bool isOnEdges() const; /* setLeftEdge/setRightEdge Set new left/right edges position, relative range. */ void setLeftEdge(Pixel p); void setRightEdge(Pixel p); void setPosition(Pixel p); bool onRightEdge; bool onLeftEdge; bool hovered; bool altered; Pixel pick; m::Action a1; m::Action a2; protected: bool m_resizable; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/actionEditor/baseActionEditor.cpp000066400000000000000000000112311425106661500236730ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/dialogs/actionEditor/baseActionEditor.h" #include "baseAction.h" #include "baseActionEditor.h" #include "core/const.h" #include "core/sequencer.h" #include "gridTool.h" #include #include namespace giada::v { geBaseActionEditor::geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h, gdBaseActionEditor* base) : Fl_Group(x, y, w, h) , m_data(nullptr) , m_base(base) , m_action(nullptr) { } /* -------------------------------------------------------------------------- */ geBaseAction* geBaseActionEditor::getActionAtCursor() const { for (int i = 0; i < children(); i++) { geBaseAction* a = static_cast(child(i)); if (a->hovered) return a; } return nullptr; } /* -------------------------------------------------------------------------- */ void geBaseActionEditor::baseDraw(bool clear) const { /* Clear the screen. */ if (clear) fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_1); /* Draw the outer container. */ fl_color(G_COLOR_GREY_4); fl_rect(x(), y(), w(), h()); /* 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) { fl_color(G_COLOR_GREY_3); drawVerticals(m_base->gridTool.getCellSize(m_data->framesInBeat)); } fl_color(G_COLOR_GREY_4); drawVerticals(m_data->framesInBeat); fl_color(G_COLOR_LIGHT_1); drawVerticals(m_data->framesInBar); /* Cover unused area. Avoid drawing cover if width == 0 (i.e. beats are 32). */ 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); } /* -------------------------------------------------------------------------- */ 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_data->framesInLoop; i += steps) { Pixel p = m_base->frameToPixel(i) + x(); 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); } } /* -------------------------------------------------------------------------- */ 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_x() < m_base->loopWidth) // Avoid click on grey area onAddAction(); } else // Prepare for dragging m_action->pick = Fl::event_x() - m_action->x(); } else if (Fl::event_button3()) { // Right button if (m_action != nullptr) { onDeleteAction(); m_action = nullptr; } } return 1; } /* -------------------------------------------------------------------------- */ int geBaseActionEditor::drag() { if (m_action == nullptr) return 0; if (m_action->isOnEdges()) onResizeAction(); else onMoveAction(); m_action->altered = true; redraw(); return 1; } /* -------------------------------------------------------------------------- */ int geBaseActionEditor::release() { int ret = 0; if (m_action != nullptr && m_action->altered) { onRefreshAction(); ret = 1; } m_action = nullptr; return ret; } } // namespace giada::vgiada-0.22.0/src/gui/elems/actionEditor/baseActionEditor.h000066400000000000000000000051041425106661500233420ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_BASE_ACTION_EDITOR_H #define GE_BASE_ACTION_EDITOR_H #include "core/types.h" #include 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 Returns the action under the mouse. nullptr if nothing found. Why not using Fl::belowmouse? It would require a boring dynamic_cast. */ geBaseAction* getActionAtCursor() const; protected: geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h, gdBaseActionEditor*); c::actionEditor::Data* m_data; /* m_base Pointer to parent class. */ gdBaseActionEditor* m_base; /* m_action Selected action. Used while dragging. */ geBaseAction* m_action; /* baseDraw Draws basic things like borders and grids. Optional background clear. */ void baseDraw(bool clear = true) const; virtual void onAddAction() = 0; virtual void onDeleteAction() = 0; virtual void onMoveAction() = 0; virtual void onResizeAction() = 0; virtual void onRefreshAction() = 0; private: /* drawVerticals Draws generic vertical lines (beats, bars, grid lines...). */ void drawVerticals(int steps) const; int push(); int drag(); int release(); }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/actionEditor/envelopeEditor.cpp000066400000000000000000000127761425106661500234570ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "envelopeEditor.h" #include "core/conf.h" #include "core/const.h" #include "envelopePoint.h" #include "glue/actionEditor.h" #include "glue/channel.h" #include "gui/dialogs/actionEditor/baseActionEditor.h" #include "src/core/actions/action.h" #include "src/core/actions/actions.h" #include "utils/log.h" #include "utils/math.h" #include #include #include namespace giada::v { geEnvelopeEditor::geEnvelopeEditor(Pixel x, Pixel y, const char* l, gdBaseActionEditor* b) : geBaseActionEditor(x, y, 200, 40, b) { copy_label(l); } /* -------------------------------------------------------------------------- */ void geEnvelopeEditor::draw() { baseDraw(); /* Print label. */ 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)); if (children() == 0) return; Pixel side = geEnvelopePoint::SIDE / 2; Pixel x1 = child(0)->x() + side; Pixel y1 = child(0)->y() + side; Pixel x2 = 0; Pixel y2 = 0; /* For each point: - paint the connecting line with the next one; - reposition it on the y axis, only if there's no point selected (dragged around). */ for (int i = 0; i < children(); i++) { geEnvelopePoint* p = static_cast(child(i)); if (m_action == nullptr) p->position(p->x(), valueToY(p->a1.event.getVelocity())); if (i > 0) { x2 = p->x() + side; y2 = p->y() + side; fl_line(x1, y1, x2, y2); x1 = x2; y1 = y2; } } draw_children(); } /* -------------------------------------------------------------------------- */ void geEnvelopeEditor::rebuild(c::actionEditor::Data& d) { m_data = &d; /* Remove all existing actions and set a new width, according to the current zoom level. */ clear(); size(m_base->fullWidth, h()); for (const m::Action& a : m_data->actions) { if (a.event.getStatus() != m::MidiEvent::ENVELOPE) continue; 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(value, 0, G_MAX_VELOCITY, y() + (h() - geEnvelopePoint::SIDE), y()); } int geEnvelopeEditor::yToValue(Pixel pixel, Pixel offset) const { return u::math::map(pixel, h() - offset, 0, 0, G_MAX_VELOCITY); } /* -------------------------------------------------------------------------- */ void geEnvelopeEditor::onAddAction() { Frame f = m_base->pixelToFrame(Fl::event_x() - x(), m_data->framesInBeat); int v = yToValue(Fl::event_y() - y()); c::actionEditor::recordEnvelopeAction(m_data->channelId, f, v); m_base->rebuild(); // TODO - USELESS } /* -------------------------------------------------------------------------- */ void geEnvelopeEditor::onDeleteAction() { c::actionEditor::deleteEnvelopeAction(m_data->channelId, m_action->a1); m_base->rebuild(); // TODO - USELESS } /* -------------------------------------------------------------------------- */ void geEnvelopeEditor::onMoveAction() { Pixel side = geEnvelopePoint::SIDE / 2; Pixel ex = Fl::event_x() - side; Pixel ey = Fl::event_y() - side; Pixel x1 = x() - side; Pixel x2 = m_base->loopWidth + x() - side; Pixel y1 = y(); Pixel y2 = y() + h() - geEnvelopePoint::SIDE; /* x-axis constraints. */ 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; m_action->position(ex, ey); redraw(); } /* -------------------------------------------------------------------------- */ void geEnvelopeEditor::onRefreshAction() { const Frame f = (m_action->x() - x()) + geEnvelopePoint::SIDE / 2; const Frame fq = m_base->pixelToFrame(f, m_data->framesInBeat); const float v = yToValue(m_action->y() - y(), geEnvelopePoint::SIDE); c::actionEditor::updateEnvelopeAction(m_data->channelId, m_action->a1, fq, v); m_base->rebuild(); } } // namespace giada::vgiada-0.22.0/src/gui/elems/actionEditor/envelopeEditor.h000066400000000000000000000035171425106661500231150ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_ENVELOPE_EDITOR_H #define GE_ENVELOPE_EDITOR_H #include "baseActionEditor.h" namespace giada::v { class geEnvelopeEditor : public geBaseActionEditor { public: geEnvelopeEditor(Pixel x, Pixel y, const char* l, gdBaseActionEditor*); void draw() override; void rebuild(c::actionEditor::Data& d) override; 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; bool isFirstPoint() const; bool isLastPoint() const; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/actionEditor/envelopePoint.cpp000066400000000000000000000030561425106661500233110ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "envelopePoint.h" #include "core/const.h" #include namespace giada::v { geEnvelopePoint::geEnvelopePoint(Pixel X, Pixel Y, m::Action 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); } } // namespace giada::vgiada-0.22.0/src/gui/elems/actionEditor/envelopePoint.h000066400000000000000000000027041425106661500227550ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_ENVELOPE_POINT_H #define GE_ENVELOPE_POINT_H #include "baseAction.h" #include "src/core/actions/actions.h" namespace giada::v { class geEnvelopePoint : public geBaseAction { public: static const Pixel SIDE = 12; geEnvelopePoint(Pixel x, Pixel y, m::Action a); void draw() override; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/actionEditor/gridTool.cpp000066400000000000000000000063041425106661500222440ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ------------------------------------------------------------------------------ * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * --------------------------------------------------------------------------- */ #include "gui/elems/actionEditor/gridTool.h" #include "core/conf.h" #include "gui/elems/basics/check.h" #include "gui/elems/basics/choice.h" #include "gui/ui.h" #include "utils/math.h" #include extern giada::v::Ui g_ui; namespace giada::v { geGridTool::geGridTool(Pixel x, Pixel y, m::Conf::Data& c) : Fl_Group(x, y, 80, 20) , m_conf(c) { gridType = new geChoice(x, y, 40, 20); gridType->addItem("1"); gridType->addItem("2"); gridType->addItem("3"); gridType->addItem("4"); gridType->addItem("6"); gridType->addItem("8"); gridType->addItem("16"); gridType->addItem("32"); gridType->showItem(0); gridType->onChange = [this](ID) { window()->redraw(); }; active = new geCheck(gridType->x() + gridType->w() + 4, y, 20, 20); gridType->showItem(m_conf.actionEditorGridVal); active->value(m_conf.actionEditorGridOn); end(); gridType->copy_tooltip(g_ui.langMapper.get(LangMap::COMMON_GRIDRES)); active->copy_tooltip(g_ui.langMapper.get(LangMap::COMMON_SNAPTOGRID)); } /* -------------------------------------------------------------------------- */ geGridTool::~geGridTool() { m_conf.actionEditorGridVal = gridType->getSelectedId(); m_conf.actionEditorGridOn = active->value(); } /* -------------------------------------------------------------------------- */ bool geGridTool::isOn() const { return active->value(); } /* -------------------------------------------------------------------------- */ int geGridTool::getValue() const { switch (gridType->getSelectedId()) { 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, Frame framesInBeat) const { if (!isOn()) return v; return u::math::quantize(v, getCellSize(framesInBeat)); } /* -------------------------------------------------------------------------- */ Frame geGridTool::getCellSize(Frame framesInBeat) const { return framesInBeat / getValue(); } } // namespace giada::vgiada-0.22.0/src/gui/elems/actionEditor/gridTool.h000066400000000000000000000033371425106661500217140ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_GRID_TOOL_H #define GE_GRID_TOOL_H #include "core/conf.h" #include "core/types.h" #include class geCheck; namespace giada::v { class geChoice; class geGridTool : public Fl_Group { public: geGridTool(Pixel x, Pixel y, m::Conf::Data&); ~geGridTool(); int getValue() const; bool isOn() const; Frame getSnapFrame(Frame f, Frame framesInBeat) const; /* getCellSize Returns the size in frames of a single cell of the grid. */ Frame getCellSize(Frame framesInBeat) const; private: m::Conf::Data& m_conf; geChoice* gridType; geCheck* active; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/actionEditor/pianoItem.cpp000066400000000000000000000051631425106661500224100ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "pianoItem.h" #include "core/const.h" #include "core/midiEvent.h" #include "src/core/actions/action.h" #include "utils/math.h" #include 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_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) { fl_rect(x(), by, w(), bh, color); fl_line(x(), by, x() + w(), by + bh); } else { Pixel vh = calcVelocityH(); 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_rectf(x(), by + (bh - vh), MIN_WIDTH, vh, color); } 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(v, 0, G_MAX_VELOCITY, 0, h() - 3); } } // namespace v } // namespace giadagiada-0.22.0/src/gui/elems/actionEditor/pianoItem.h000066400000000000000000000030271425106661500220520ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_PIANO_ITEM_H #define GE_PIANO_ITEM_H #include "baseAction.h" namespace giada::m { struct Action; } namespace giada::v { 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: bool m_ringLoop; bool m_orphaned; Pixel calcVelocityH() const; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/actionEditor/pianoRoll.cpp000066400000000000000000000233311425106661500224170ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "pianoRoll.h" #include "core/conf.h" #include "core/const.h" #include "core/midiEvent.h" #include "glue/actionEditor.h" #include "glue/channel.h" #include "gui/dialogs/actionEditor/baseActionEditor.h" #include "gui/elems/actionEditor/pianoItem.h" #include "src/core/actions/action.h" #include "utils/log.h" #include "utils/math.h" #include "utils/string.h" #include #include namespace giada::v { gePianoRoll::gePianoRoll(Pixel X, Pixel Y, gdBaseActionEditor* b) : geBaseActionEditor(X, Y, 200, CELL_H * MAX_KEYS, b) , m_pick(0) { } /* -------------------------------------------------------------------------- */ void gePianoRoll::drawSurfaceY() { surfaceY = fl_create_offscreen(CELL_W, h()); fl_begin_offscreen(surfaceY); /* Warning: only w() and h() come from this widget, x and y coordinates are absolute, since we are writing in a memory chunk. */ fl_rectf(0, 0, CELL_W, h(), G_COLOR_GREY_1); fl_line_style(FL_DASH, 0, nullptr); fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE); int octave = MAX_OCTAVES; 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; } /* 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)); /* Print horizontal line */ 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::drawSurfaceX() { surfaceX = fl_create_offscreen(CELL_W, h()); fl_begin_offscreen(surfaceX); fl_rectf(0, 0, CELL_W, h(), G_COLOR_GREY_1); 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 static_cast(Notes::G): case static_cast(Notes::E): case static_cast(Notes::D): case static_cast(Notes::B): case static_cast(Notes::A): fl_rectf(0, i * CELL_H, CELL_W, CELL_H, G_COLOR_GREY_2); break; } if (i < MAX_KEYS + 1) { fl_color(G_COLOR_GREY_3); fl_line(0, i * CELL_H, CELL_W, i * CELL_H); } } fl_line_style(0); fl_end_offscreen(); } /* -------------------------------------------------------------------------- */ void gePianoRoll::draw() { fl_copy_offscreen(x(), y(), CELL_W, h(), surfaceY, 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(), surfaceX, 1, 0); #else for (Pixel i = CELL_W; i < m_base->loopWidth; i += CELL_W) fl_copy_offscreen(x() + i, y(), CELL_W, h(), surfaceX, 0, 0); #endif baseDraw(false); draw_children(); } /* -------------------------------------------------------------------------- */ int gePianoRoll::handle(int e) { if (!Fl::event_button3()) return geBaseActionEditor::handle(e); switch (e) { case FL_PUSH: { m_pick = Fl::event_y() - y(); break; } case FL_DRAG: { const int pos = Fl::event_y() - m_pick; const int min = parent()->y(); const int max = -h() + (parent()->h() + parent()->y()); position(x(), std::clamp(pos, max, min)); break; } default: break; } return geBaseActionEditor::handle(e); } /* -------------------------------------------------------------------------- */ void gePianoRoll::onAddAction() { Frame frame = m_base->pixelToFrame(Fl::event_x() - x(), m_data->framesInBeat); int note = yToNote(Fl::event_y() - y()); c::actionEditor::recordMidiAction(m_data->channelId, note, G_MAX_VELOCITY, frame); 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 } /* -------------------------------------------------------------------------- */ 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 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; m_action->position(ex, ey); } /* -------------------------------------------------------------------------- */ void gePianoRoll::onResizeAction() { if (!static_cast(m_action)->isResizable()) return; 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 (m_action->onRightEdge) m_action->setRightEdge(ex - m_action->x()); else m_action->setLeftEdge(ex); } /* -------------------------------------------------------------------------- */ void gePianoRoll::onRefreshAction() { namespace ca = c::actionEditor; Pixel p1 = m_action->x() - x(); Pixel p2 = m_action->x() + m_action->w() - x(); Frame f1 = 0; Frame f2 = 0; if (!m_action->isOnEdges()) { f1 = m_base->pixelToFrame(p1, m_data->framesInBeat); f2 = m_base->pixelToFrame(p2, m_data->framesInBeat, /*snap=*/false) - (m_base->pixelToFrame(p1, m_data->framesInBeat, /*snap=*/false) - f1); } else if (m_action->onLeftEdge) { f1 = m_base->pixelToFrame(p1, m_data->framesInBeat); f2 = m_action->a2.frame; if (f1 == f2) // If snapping makes an action fall onto the other f1 -= G_DEFAULT_ACTION_SIZE; } else if (m_action->onRightEdge) { f1 = m_action->a1.frame; f2 = m_base->pixelToFrame(p2, m_data->framesInBeat); if (f1 == f2) // If snapping makes an action fall onto the other f2 += G_DEFAULT_ACTION_SIZE; } assert(f2 != 0); int note = yToNote(m_action->y() - y()); int velocity = m_action->a1.event.getVelocity(); ca::updateMidiAction(m_data->channelId, m_action->a1, note, velocity, f1, f2); 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 return m_base->loopWidth - (px - x()); return m_base->frameToPixel(a2.frame - a1.frame); } return geBaseAction::MIN_WIDTH; // Orphaned } /* -------------------------------------------------------------------------- */ void gePianoRoll::rebuild(c::actionEditor::Data& d) { m_data = &d; /* Remove all existing actions and set a new width, according to the current zoom level. */ clear(); size(m_base->fullWidth, (MAX_KEYS + 1) * CELL_H); for (const m::Action& a1 : m_data->actions) { if (a1.event.getStatus() == m::MidiEvent::NOTE_OFF) continue; assert(a1.isValid()); // a2 might be null if orphaned const m::Action& a2 = a1.next != nullptr ? *a1.next : m::Action{}; Pixel px = x() + m_base->frameToPixel(a1.frame); Pixel py = y() + noteToY(a1.event.getNote()); Pixel ph = CELL_H; Pixel pw = getPianoItemW(px, a1, a2); add(new gePianoItem(px, py, pw, ph, a1, a2)); } drawSurfaceY(); drawSurfaceX(); redraw(); } } // namespace giada::v giada-0.22.0/src/gui/elems/actionEditor/pianoRoll.h000066400000000000000000000056271425106661500220740ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_PIANO_ROLL_H #define GE_PIANO_ROLL_H #include "baseActionEditor.h" #include namespace giada::m { struct Action; } 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; gePianoRoll(Pixel x, Pixel y, gdBaseActionEditor* b); void draw() override; int handle(int e) override; void rebuild(c::actionEditor::Data& d) override; 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 }; void onAddAction() override; void onDeleteAction() override; void onMoveAction() override; void onResizeAction() override; void onRefreshAction() override; /* drawSurface* Generates a complex drawing in memory first and copy it to the screen at a later point in time. Fl_Offscreen surface holds the necessary data. The first call creates an offscreen surface of CELL_W pixel wide containing note values. The second call creates another offscreen surface of CELL_W pixels wide containing the rest of the piano roll. The latter will then be tiled during the ::draw() call. */ void drawSurfaceY(); void drawSurfaceX(); Pixel snapToY(Pixel p) const; int yToNote(Pixel y) const; Pixel noteToY(int n) const; Pixel getPianoItemW(Pixel x, const m::Action& a1, const m::Action& a2) const; Fl_Offscreen surfaceY; // vertical notes, no x-repeat Fl_Offscreen surfaceX; // lines, x-repeat /* m_pick Y-coordinate of the click event when the user clicks on an empty area of the piano roll. Used for right mouse button scrolling. */ Pixel m_pick; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/actionEditor/sampleAction.cpp000066400000000000000000000042031425106661500230740ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "sampleAction.h" #include "core/const.h" #include "src/core/actions/action.h" #include 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) { } /* -------------------------------------------------------------------------- */ void geSampleAction::draw() { Fl_Color color = hovered ? G_COLOR_LIGHT_2 : G_COLOR_LIGHT_1; if (m_singlePress) { fl_rectf(x(), y(), w(), h(), color); } else { if (a1.event.getStatus() == m::MidiEvent::NOTE_KILL) fl_rect(x(), y(), MIN_WIDTH, h(), color); else { fl_rectf(x(), y(), MIN_WIDTH, h(), color); if (a1.event.getStatus() == m::MidiEvent::NOTE_ON) 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); } } } } // namespace v } // namespace giada giada-0.22.0/src/gui/elems/actionEditor/sampleAction.h000066400000000000000000000027671425106661500225560ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_SAMPLE_ACTION_H #define GE_SAMPLE_ACTION_H #include "baseAction.h" #include "src/core/actions/actions.h" namespace giada::v { class geSampleAction : public geBaseAction { public: geSampleAction(Pixel x, Pixel y, Pixel w, Pixel h, bool singlePress, m::Action a1, m::Action a2); void draw() override; private: bool m_singlePress; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/actionEditor/sampleActionEditor.cpp000066400000000000000000000141301425106661500242430ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/actionEditor/sampleActionEditor.h" #include "core/const.h" #include "glue/actionEditor.h" #include "glue/channel.h" #include "gui/dialogs/actionEditor/baseActionEditor.h" #include "gui/dialogs/actionEditor/sampleActionEditor.h" #include "gui/ui.h" #include "sampleAction.h" #include "src/core/actions/action.h" #include "src/core/actions/actions.h" #include "utils/log.h" #include #include #include extern giada::v::Ui g_ui; namespace giada::v { geSampleActionEditor::geSampleActionEditor(Pixel x, Pixel y, gdBaseActionEditor* b) : geBaseActionEditor(x, y, 200, 40, b) { } /* -------------------------------------------------------------------------- */ void geSampleActionEditor::rebuild(c::actionEditor::Data& d) { m_data = &d; bool isSinglePressMode = m_data->sample->channelMode == SamplePlayerMode::SINGLE_PRESS; bool isAnyLoopMode = m_data->sample->isLoopMode; /* Remove all existing actions and set a new width, according to the current zoom level. */ clear(); size(m_base->fullWidth, h()); for (const m::Action& a1 : m_data->actions) { if (a1.event.getStatus() == m::MidiEvent::ENVELOPE || isNoteOffSinglePress(a1)) continue; const m::Action& a2 = a1.next != nullptr ? *a1.next : m::Action{}; Pixel px = x() + m_base->frameToPixel(a1.frame); Pixel py = y() + 4; Pixel pw = 0; Pixel ph = h() - 8; if (a2.isValid() && isSinglePressMode) pw = m_base->frameToPixel(a2.frame - a1.frame); geSampleAction* gsa = new geSampleAction(px, py, pw, ph, isSinglePressMode, a1, a2); add(gsa); resizable(gsa); } /* If channel is LOOP_ANY, deactivate it: a loop mode channel cannot hold keypress/keyrelease actions. */ isAnyLoopMode ? deactivate() : activate(); redraw(); } /* -------------------------------------------------------------------------- */ void geSampleActionEditor::draw() { /* Force height to match its parent's height. This widget belongs to a geScroll container (see geSplitScroll class in baseActionEditor.h) but there's nothing to scroll here actually. */ size(w(), parent()->h()); /* Draw basic boundaries (+ beat bars) and hide the unused area. Then draw children (the actions). */ baseDraw(); /* Print label. */ fl_color(G_COLOR_GREY_4); fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE); if (active()) fl_draw(g_ui.langMapper.get(LangMap::ACTIONEDITOR_STARTSTOP), x() + 4, y(), w(), h(), (Fl_Align)(FL_ALIGN_LEFT | FL_ALIGN_CENTER)); else fl_draw(g_ui.langMapper.get(LangMap::ACTIONEDITOR_STARTSTOPDISABLED), x() + 4, y(), w(), h(), (Fl_Align)(FL_ALIGN_LEFT | FL_ALIGN_CENTER)); draw_children(); } /* -------------------------------------------------------------------------- */ void geSampleActionEditor::onAddAction() { Frame f = m_base->pixelToFrame(Fl::event_x() - x(), m_data->framesInBeat); c::actionEditor::recordSampleAction(m_data->channelId, static_cast(m_base)->getActionType(), f); } /* -------------------------------------------------------------------------- */ void geSampleActionEditor::onDeleteAction() { c::actionEditor::deleteSampleAction(m_data->channelId, m_action->a1); } /* -------------------------------------------------------------------------- */ 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; m_action->setPosition(ex); } /* -------------------------------------------------------------------------- */ 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 (m_action->onRightEdge) m_action->setRightEdge(ex - m_action->x()); else m_action->setLeftEdge(ex); } /* -------------------------------------------------------------------------- */ void geSampleActionEditor::onRefreshAction() { namespace ca = c::actionEditor; Pixel p1 = m_action->x() - x(); Pixel p2 = m_action->x() + m_action->w() - x(); Frame f1 = 0; Frame f2 = 0; int type = m_action->a1.event.getStatus(); if (!m_action->isOnEdges()) { f1 = m_base->pixelToFrame(p1, m_data->framesInBeat); f2 = m_base->pixelToFrame(p2, m_data->framesInBeat, /*snap=*/false) - (m_base->pixelToFrame(p1, m_data->framesInBeat, /*snap=*/false) - f1); } else if (m_action->onLeftEdge) { f1 = m_base->pixelToFrame(p1, m_data->framesInBeat); f2 = m_action->a2.frame; } else if (m_action->onRightEdge) { f1 = m_action->a1.frame; f2 = m_base->pixelToFrame(p2, m_data->framesInBeat); } 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 && a.event.getStatus() == m::MidiEvent::NOTE_OFF; } } // namespace giada::vgiada-0.22.0/src/gui/elems/actionEditor/sampleActionEditor.h000066400000000000000000000033641425106661500237170ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_SAMPLE_ACTION_EDITOR_H #define GE_SAMPLE_ACTION_EDITOR_H #include "baseActionEditor.h" namespace giada::m { struct Action; } namespace giada::v { class geSampleAction; class geSampleActionEditor : public geBaseActionEditor { public: geSampleActionEditor(Pixel x, Pixel y, gdBaseActionEditor*); void draw() override; void rebuild(c::actionEditor::Data& d) override; private: void onAddAction() override; void onDeleteAction() override; void onMoveAction() override; void onResizeAction() override; void onRefreshAction() override; bool isNoteOffSinglePress(const m::Action& a); }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/actionEditor/splitScroll.cpp000066400000000000000000000055111425106661500227720ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ------------------------------------------------------------------------------ * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * --------------------------------------------------------------------------- */ #include "splitScroll.h" namespace giada::v { geSplitScroll::geSplitScroll(Pixel x, Pixel y, Pixel w, Pixel h) : geSplit(x, y, w, h) , m_a(0, 0, 0, 0, Fl_Scroll::VERTICAL_ALWAYS) , m_b(0, 0, 0, 0, Direction::HORIZONTAL) { m_b.onScrollH = [&a = m_a](Pixel x) { a.scroll_to(x, a.yposition()); }; } /* -------------------------------------------------------------------------- */ void geSplitScroll::addWidgets(Fl_Widget& wa, Fl_Widget& wb, Pixel topContentH) { m_a.add(&wa); m_b.addWidget(&wb); init(m_a, m_b); if (topContentH != -1) resizePanel(geSplit::Panel::A, topContentH); } /* -------------------------------------------------------------------------- */ Pixel geSplitScroll::getScrollX() const { return m_b.xposition(); } Pixel geSplitScroll::getScrollY() const { return m_a.yposition(); } /* -------------------------------------------------------------------------- */ Pixel geSplitScroll::getContentWidth() const { if (m_a.countChildren() == 0) return 0; return m_a.child(0)->w(); } /* -------------------------------------------------------------------------- */ Pixel geSplitScroll::getTopContentH() const { return m_a.h(); } /* -------------------------------------------------------------------------- */ geompp::Rect geSplitScroll::getBoundsNoScrollbar() const { return { x(), y(), w() - m_a.scrollbar.w() - G_GUI_OUTER_MARGIN, h() - m_b.hscrollbar.h() - G_GUI_OUTER_MARGIN}; } /* -------------------------------------------------------------------------- */ void geSplitScroll::setScrollX(Pixel p) { p = std::max(0, p); m_a.scroll_to(p, m_a.yposition()); m_b.scroll_to(p, m_b.yposition()); } void geSplitScroll::setScrollY(Pixel p) { m_a.scroll_to(m_a.xposition(), p); } } // namespace giada::vgiada-0.22.0/src/gui/elems/actionEditor/splitScroll.h000066400000000000000000000036061425106661500224420ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_SPLITSCROLL_H #define GE_SPLITSCROLL_H #include "core/types.h" #include "deps/geompp/src/rect.hpp" #include "gui/elems/basics/liquidScroll.h" #include "gui/elems/basics/scroll.h" #include "gui/elems/basics/split.h" namespace giada::v { class geSplitScroll : public geSplit { public: geSplitScroll(Pixel x, Pixel y, Pixel w, Pixel h); Pixel getScrollX() const; Pixel getScrollY() const; Pixel getContentWidth() const; Pixel getTopContentH() const; geompp::Rect getBoundsNoScrollbar() const; void addWidgets(Fl_Widget& a, Fl_Widget& b, Pixel topContentH = -1); void setScrollX(Pixel p); void setScrollY(Pixel p); private: geScroll m_a; geLiquidScroll m_b; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/actionEditor/velocityEditor.cpp000066400000000000000000000100671425106661500234670ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "velocityEditor.h" #include "core/conf.h" #include "core/const.h" #include "envelopePoint.h" #include "glue/actionEditor.h" #include "gui/dialogs/actionEditor/baseActionEditor.h" #include "gui/ui.h" #include "src/core/actions/action.h" #include "utils/log.h" #include "utils/math.h" #include #include #include extern giada::v::Ui g_ui; namespace giada::v { geVelocityEditor::geVelocityEditor(Pixel x, Pixel y, gdBaseActionEditor* b) : geBaseActionEditor(x, y, 200, 40, b) { } /* -------------------------------------------------------------------------- */ void geVelocityEditor::draw() { baseDraw(); if (h() < geEnvelopePoint::SIDE) return; /* Print label. */ fl_color(G_COLOR_GREY_4); fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE); fl_draw(g_ui.langMapper.get(LangMap::ACTIONEDITOR_VELOCITY), x() + 4, y(), w(), h(), FL_ALIGN_LEFT); if (children() == 0) return; const Pixel side = geEnvelopePoint::SIDE / 2; for (int i = 0; i < children(); i++) { geEnvelopePoint* p = static_cast(child(i)); if (m_action == nullptr) p->position(p->x(), valueToY(p->a1.event.getVelocity())); const Pixel x1 = p->x() + side; const Pixel y1 = p->y(); const Pixel y2 = y() + h(); fl_line(x1, y1, x1, y2); } 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(v, 0, G_MAX_VELOCITY, y() + (h() - geEnvelopePoint::SIDE), y()); } int geVelocityEditor::yToValue(Pixel px) const { return u::math::map(px, h() - geEnvelopePoint::SIDE, 0, 0, G_MAX_VELOCITY); } /* -------------------------------------------------------------------------- */ void geVelocityEditor::rebuild(c::actionEditor::Data& d) { m_data = &d; /* Remove all existing actions and set a new width, according to the current zoom level. */ clear(); size(m_base->fullWidth, h()); for (const m::Action& action : m_data->actions) { if (action.event.getStatus() == m::MidiEvent::NOTE_OFF) continue; Pixel px = x() + m_base->frameToPixel(action.frame); Pixel py = y() + valueToY(action.event.getVelocity()); add(new geEnvelopePoint(px, py, action)); } resizable(nullptr); redraw(); } /* -------------------------------------------------------------------------- */ 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; m_action->position(m_action->x(), ey); redraw(); } /* -------------------------------------------------------------------------- */ void geVelocityEditor::onRefreshAction() { c::actionEditor::updateVelocity(m_action->a1, yToValue(m_action->y() - y())); m_base->rebuild(); // Rebuild pianoRoll as well } } // namespace giada::vgiada-0.22.0/src/gui/elems/actionEditor/velocityEditor.h000066400000000000000000000033201425106661500231260ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_VELOCITY_EDITOR_H #define GE_VELOCITY_EDITOR_H #include "baseActionEditor.h" namespace giada::v { class geEnvelopePoint; class geVelocityEditor : public geBaseActionEditor { public: geVelocityEditor(Pixel x, Pixel y, gdBaseActionEditor*); void draw() override; void rebuild(c::actionEditor::Data& d) override; private: void onMoveAction() override; void onRefreshAction() override; void onAddAction() override{}; void onDeleteAction() override{}; void onResizeAction() override{}; Pixel valueToY(int v) const; int yToValue(Pixel y) const; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/basics/000077500000000000000000000000001425106661500165725ustar00rootroot00000000000000giada-0.22.0/src/gui/elems/basics/box.cpp000066400000000000000000000041701425106661500200700ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "box.h" #include "core/const.h" #include "utils/gui.h" #include namespace giada::v { geBox::geBox(int x, int y, int w, int h, const char* l, Fl_Align al) : Fl_Box(x, y, w, h) { copy_label(l); box(FL_NO_BOX); color(G_COLOR_GREY_1); align(al | FL_ALIGN_INSIDE); } /* -------------------------------------------------------------------------- */ geBox::geBox(const char* l, Fl_Align al) : geBox(0, 0, 0, 0, l, al) { } /* -------------------------------------------------------------------------- */ void geBox::draw() { fl_rectf(x(), y(), w(), h(), color()); // Clear background if (box() != FL_NO_BOX) 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) { fl_color(active() ? G_COLOR_LIGHT_2 : G_COLOR_GREY_4); fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE); fl_draw(giada::u::gui::truncate(label(), w()).c_str(), x(), y(), w(), h(), align()); } } } // namespace giada::vgiada-0.22.0/src/gui/elems/basics/box.h000066400000000000000000000027021425106661500175340ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_BOX_H #define GE_BOX_H #include namespace giada::v { 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(const char* l = nullptr, Fl_Align al = FL_ALIGN_CENTER); void draw() override; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/basics/boxtypes.cpp000066400000000000000000000032541425106661500211570ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * boxtypes * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "boxtypes.h" #include "../../../core/const.h" #include 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); } 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); } 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); } giada-0.22.0/src/gui/elems/basics/boxtypes.h000066400000000000000000000031661425106661500206260ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * boxtypes * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_BOXTYPES_H #define GE_BOXTYPES_H #include constexpr Fl_Boxtype G_CUSTOM_BORDER_BOX = FL_FREE_BOXTYPE; constexpr Fl_Boxtype G_CUSTOM_UP_BOX = static_cast(FL_FREE_BOXTYPE + 1); constexpr Fl_Boxtype G_CUSTOM_DOWN_BOX = static_cast(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); #endif giada-0.22.0/src/gui/elems/basics/browser.cpp000066400000000000000000000037711425106661500207710ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * geScroll * Custom scroll with nice scrollbars and something else. * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/basics/browser.h" #include "core/const.h" #include "gui/elems/basics/boxtypes.h" namespace giada::v { geBrowser::geBrowser(int x, int y, int w, int h) : Fl_Browser(x, y, w, h) { box(G_CUSTOM_BORDER_BOX); textsize(G_GUI_FONT_SIZE_BASE); textcolor(G_COLOR_LIGHT_2); selection_color(G_COLOR_GREY_4); color(G_COLOR_GREY_2); scrollbar.color(G_COLOR_GREY_2); scrollbar.selection_color(G_COLOR_GREY_4); scrollbar.labelcolor(G_COLOR_LIGHT_1); scrollbar.slider(G_CUSTOM_BORDER_BOX); hscrollbar.color(G_COLOR_GREY_2); hscrollbar.selection_color(G_COLOR_GREY_4); hscrollbar.labelcolor(G_COLOR_LIGHT_1); hscrollbar.slider(G_CUSTOM_BORDER_BOX); } /* -------------------------------------------------------------------------- */ geBrowser::geBrowser() : geBrowser(0, 0, 0, 0) { } } // namespace giada::vgiada-0.22.0/src/gui/elems/basics/browser.h000066400000000000000000000025301425106661500204260ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_BROWSER_H #define GE_BROWSER_H #include namespace giada::v { class geBrowser : public Fl_Browser { public: geBrowser(int x, int y, int w, int h); geBrowser(); }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/basics/button.cpp000066400000000000000000000056411425106661500206170ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "button.h" #include "core/const.h" #include "utils/gui.h" #include namespace giada::v { 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) , onClick(nullptr) , 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) { callback(cb_click); } /* -------------------------------------------------------------------------- */ geButton::geButton(const char* l, const char** imgOff, const char** imgOn, const char** imgDisabled) : geButton(0, 0, 0, 0, l, imgOff, imgOn, imgDisabled) { } /* -------------------------------------------------------------------------- */ void geButton::cb_click(Fl_Widget* w, void*) { geButton* b = static_cast(w); if (b->onClick != nullptr) b->onClick(); } /* -------------------------------------------------------------------------- */ void geButton::draw() { //Fl_Button::draw(); if (active()) 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 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_color(textColor); 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); } } } // namespace giada::vgiada-0.22.0/src/gui/elems/basics/button.h000066400000000000000000000040331425106661500202560ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * geButton * A regular button. * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_BUTTON_H #define GE_BUTTON_H #include #include namespace giada::v { 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(const char* l = nullptr, const char** imgOff = nullptr, const char** imgOn = nullptr, const char** imgDisabled = nullptr); void draw() override; std::function onClick; protected: static void cb_click(Fl_Widget*, void*); 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 }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/basics/check.cpp000066400000000000000000000050061425106661500203540ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "check.h" #include "core/const.h" #include #include 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(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 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(), boxColor); else 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(), textAlign); } /* -------------------------------------------------------------------------- */ bool geCheck::hasMultilineText() const { return label() == nullptr ? false : std::strchr(label(), '\n') != nullptr; } giada-0.22.0/src/gui/elems/basics/check.h000066400000000000000000000030101425106661500200120ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_CHECK_H #define GE_CHECK_H #include #include class geCheck : public Fl_Check_Button { public: geCheck(int x, int y, int w, int h, const char* l = 0); void draw() override; std::function onChange = nullptr; private: static void cb_onChange(Fl_Widget* w, void* p); void cb_onChange(); bool hasMultilineText() const; }; #endif giada-0.22.0/src/gui/elems/basics/choice.cpp000066400000000000000000000113661425106661500205370ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/basics/choice.h" #include "core/const.h" #include "gui/drawing.h" #include "utils/gui.h" #include "utils/vector.h" #include #include namespace giada::v { geChoice::geMenu::geMenu(int x, int y, int w, int h) : Fl_Choice(x, y, w, h) { labelsize(G_GUI_FONT_SIZE_BASE); labelcolor(G_COLOR_LIGHT_2); box(FL_BORDER_BOX); textsize(G_GUI_FONT_SIZE_BASE); textcolor(G_COLOR_LIGHT_2); color(G_COLOR_GREY_2); } /* -------------------------------------------------------------------------- */ void geChoice::geMenu::draw() { geompp::Rect bounds(x(), y(), w(), h()); drawRectf(bounds, G_COLOR_GREY_2); // background drawRect(bounds, static_cast(G_COLOR_GREY_4)); // border fl_polygon(x() + w() - 8, y() + h() - 1, x() + w() - 1, y() + h() - 8, x() + w() - 1, y() + h() - 1); if (value() != -1) drawText(text(value()), bounds, active() ? G_COLOR_LIGHT_2 : G_COLOR_GREY_4); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ geChoice::geChoice(int x, int y, int w, int h, const char* l, int labelWidth) : geFlex(x, y, w, h, Direction::HORIZONTAL, G_GUI_INNER_MARGIN) , m_text(nullptr) , m_menu(nullptr) { if (l != nullptr) { m_text = new geBox(l, FL_ALIGN_RIGHT); add(m_text, labelWidth != 0 ? labelWidth : u::gui::getStringRect(l).w); } m_menu = new geMenu(x, y, w, h); add(m_menu); end(); } /* -------------------------------------------------------------------------- */ geChoice::geChoice(const char* l, int labelWidth) : geChoice(0, 0, 0, 0, l, labelWidth) { } /* -------------------------------------------------------------------------- */ void geChoice::cb_onChange(Fl_Widget* /*w*/, void* p) { (static_cast(p))->cb_onChange(); } /* -------------------------------------------------------------------------- */ void geChoice::cb_onChange() { if (onChange != nullptr) onChange(getSelectedId()); } /* -------------------------------------------------------------------------- */ ID geChoice::getSelectedId() const { return m_menu->value() == -1 ? -1 : m_ids.at(m_menu->value()); } /* -------------------------------------------------------------------------- */ void geChoice::addItem(const std::string& label, ID id) { m_menu->add(label.c_str(), 0, cb_onChange, static_cast(this)); if (id != -1) m_ids.push_back(id); else // auto-increment m_ids.push_back(m_ids.size() == 0 ? 0 : m_ids.back() + 1); } /* -------------------------------------------------------------------------- */ void geChoice::showItem(const std::string& label) { m_menu->value(m_menu->find_index(label.c_str())); } void geChoice::showItem(ID id) { m_menu->value(u::vector::indexOf(m_ids, id)); } /* -------------------------------------------------------------------------- */ void geChoice::activate() { geFlex::activate(); m_menu->activate(); if (m_text != nullptr) m_text->activate(); } void geChoice::deactivate() { geFlex::deactivate(); m_menu->deactivate(); if (m_text != nullptr) m_text->deactivate(); } /* -------------------------------------------------------------------------- */ std::string geChoice::getSelectedLabel() const { return m_menu->text(); } /* -------------------------------------------------------------------------- */ std::size_t geChoice::countItems() const { return m_ids.size(); } /* -------------------------------------------------------------------------- */ void geChoice::clear() { m_menu->clear(); m_ids.clear(); } } // namespace giada::vgiada-0.22.0/src/gui/elems/basics/choice.h000066400000000000000000000046361425106661500202060ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_CHOICE_H #define GE_CHOICE_H #include "core/types.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/flex.h" #include #include #include #include namespace giada::v { class geChoice : public geFlex { public: /* geChoice Constructors. If label is != nullptr but labelWidth is not specified, the label width is automatically computed and adjusted accordingly. */ geChoice(int x, int y, int w, int h, const char* l = nullptr, int labelWidth = 0); geChoice(const char* l = nullptr, int labelWidth = 0); ID getSelectedId() const; std::string getSelectedLabel() const; std::size_t countItems() const; /* addItem Adds a new item with a certain ID. Pass id = -1 to auto-increment it (ID starts from 0). */ void addItem(const std::string& label, ID id = -1); void showItem(const std::string& label); void showItem(ID); void activate(); void deactivate(); void clear(); std::function onChange = nullptr; private: class geMenu : public Fl_Choice { public: geMenu(int x, int y, int w, int h); void draw() override; }; static void cb_onChange(Fl_Widget* w, void* p); void cb_onChange(); geBox* m_text; geMenu* m_menu; std::vector m_ids; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/basics/dial.cpp000066400000000000000000000044001425106661500202050ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/basics/dial.h" #include "core/const.h" #include namespace giada::v { geDial::geDial(int x, int y, int w, int h, const char* l) : Fl_Dial(x, y, w, h, l) , onChange(nullptr) { 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 callback(cb_change); } /* -------------------------------------------------------------------------- */ void geDial::cb_change(Fl_Widget* w, void*) { geDial* d = static_cast(w); if (d->onChange != nullptr) d->onChange(d->value()); } /* -------------------------------------------------------------------------- */ void geDial::draw() { 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_4); fl_arc(x(), y(), w(), h(), 0, 360); fl_pie(x(), y(), w(), h(), 270 - angle, 270 - angle1()); } } // namespace giada::vgiada-0.22.0/src/gui/elems/basics/dial.h000066400000000000000000000027251425106661500176620ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_DIAL_H #define GE_DIAL_H #include #include namespace giada::v { class geDial : public Fl_Dial { public: geDial(int x, int y, int w, int h, const char* l = 0); void draw() override; std::function onChange; private: static void cb_change(Fl_Widget*, void*); }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/basics/flex.cpp000066400000000000000000000111571425106661500202410ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "flex.h" #include #include namespace giada::v { geFlex::Elem::Elem(Fl_Widget& w, geFlex& parent, Direction d, int size, geompp::Border pad) : size(size) , m_w(w) , m_parent(parent) , m_dir(d) , m_pad(pad) { } /* -------------------------------------------------------------------------- */ int geFlex::Elem::getSize() const { if (isFixed()) return size; return m_dir == Direction::VERTICAL ? m_w.h() : m_w.w(); } /* -------------------------------------------------------------------------- */ bool geFlex::Elem::isFixed() const { return size != -1; } /* -------------------------------------------------------------------------- */ void geFlex::Elem::resize(int pos, int newSize) { geompp::Rect bounds; if (m_dir == Direction::VERTICAL) bounds = geompp::Rect(m_parent.x(), pos, m_parent.w(), newSize).reduced(m_pad); else bounds = geompp::Rect(pos, m_parent.y(), newSize, m_parent.h()).reduced(m_pad); m_w.resize(bounds.x, bounds.y, bounds.w, bounds.h); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ geFlex::geFlex(int x, int y, int w, int h, Direction d, int gutter) : Fl_Group(x, y, w, h, 0) , m_direction(d) , m_gutter(gutter) , m_numFixed(0) { Fl_Group::end(); } /* -------------------------------------------------------------------------- */ geFlex::geFlex(geompp::Rect r, Direction d, int gutter) : geFlex(r.x, r.y, r.w, r.h, d, gutter) { } /* -------------------------------------------------------------------------- */ geFlex::geFlex(Direction d, int gutter) : geFlex(0, 0, 0, 0, d, gutter) { } /* -------------------------------------------------------------------------- */ void geFlex::add(Fl_Widget& w, int size, geompp::Border pad) { Fl_Group::add(w); m_elems.push_back({w, *this, m_direction, size, pad}); if (size != -1) m_numFixed++; } void geFlex::add(Fl_Widget* w, int size, geompp::Border pad) { geFlex::add(*w, size, pad); } /* -------------------------------------------------------------------------- */ void geFlex::resize(int X, int Y, int W, int H) { Fl_Group::resize(X, Y, W, H); const size_t numAllElems = m_elems.size(); const size_t numLiquidElems = numAllElems - m_numFixed; const int pos = m_direction == Direction::VERTICAL ? y() : x(); const int size = m_direction == Direction::VERTICAL ? h() : w(); /* No fancy computations if there are no liquid elements. Just lay children according to their fixed size. */ if (numLiquidElems == 0) { layWidgets(pos); return; } const int fixedElemsSize = std::accumulate(m_elems.begin(), m_elems.end(), 0, [](int acc, const Elem& e) { return e.isFixed() ? acc + e.getSize() : acc; }); const int availableSize = size - (m_gutter * (numAllElems - 1)); // Total size - gutters const int liquidElemSize = (availableSize - fixedElemsSize) / numLiquidElems; layWidgets(pos, liquidElemSize); } /* -------------------------------------------------------------------------- */ void geFlex::layWidgets(int startPos, int sizeIfLiquid) { int nextElemPos = startPos; for (Elem& e : m_elems) { e.resize(nextElemPos, e.isFixed() ? e.size : sizeIfLiquid); nextElemPos += e.getSize() + m_gutter; } } /* -------------------------------------------------------------------------- */ void geFlex::end() { Fl_Group::end(); resize(x(), y(), w(), h()); } } // namespace giada::v giada-0.22.0/src/gui/elems/basics/flex.h000066400000000000000000000052401425106661500177020ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_FLEX_H #define GE_FLEX_H #include "deps/geompp/src/border.hpp" #include "deps/geompp/src/rect.hpp" #include "gui/types.h" #include #include namespace giada::v { /* geFlex Like a FlexBox item, it's a group that contains widgets that can be stretched to fill the area. Inspired by https://github.com/osen/FL_Flex. */ class geFlex : public Fl_Group { public: geFlex(int x, int y, int w, int h, Direction d, int gutter = 0); geFlex(geompp::Rect, Direction d, int gutter = 0); geFlex(Direction d, int gutter = 0); /* add Adds an existing widget to the Flex layout. If 'size' == -1, the widget will be stretched to take up the available space. WARNING: like Fl_Group, geFlex owns widgets! */ void add(Fl_Widget&, int size = -1, geompp::Border pad = {}); void add(Fl_Widget*, int size = -1, geompp::Border pad = {}); /* end Finalize the Flex item. Call this when you're done add()ing widgets. */ void end(); private: class Elem { public: Elem(Fl_Widget&, geFlex& parent, Direction, int size, geompp::Border pad); int getSize() const; bool isFixed() const; void resize(int pos, int size); int size; private: Fl_Widget& m_w; geFlex& m_parent; Direction m_dir; geompp::Border m_pad; }; void resize(int x, int y, int w, int h) override; void layWidgets(int startPos, int sizeIfLiquid = 0); Direction m_direction; int m_gutter; std::vector m_elems; int m_numFixed; }; } // namespace giada::v #endifgiada-0.22.0/src/gui/elems/basics/group.cpp000066400000000000000000000047201425106661500204350ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * geScroll * Custom scroll with nice scrollbars and something else. * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "group.h" #include #include namespace giada { namespace v { geGroup::geGroup(int x, int y) : Fl_Group(x, y, 0, 0) { end(); } /* -------------------------------------------------------------------------- */ std::size_t geGroup::countChildren() const { 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); int newW = 0; int newH = 0; 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); } /* -------------------------------------------------------------------------- */ Fl_Widget* geGroup::getChild(std::size_t i) { 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 } } // namespace v } // namespace giada giada-0.22.0/src/gui/elems/basics/group.h000066400000000000000000000045431425106661500201050ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * geScroll * Custom scroll with nice scrollbars and something else. * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_GROUP_H #define GE_GROUP_H #include #include #include namespace giada { namespace v { /* geGroup A group that resizes itself according to the content. */ class geGroup : public Fl_Group { public: geGroup(int x, int y); /* countChildren Returns the number of widgets contained in this group. */ 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). As with any other FLTK group, the widget becomes owned by this group: If you add static or automatic (local) variables, then it is your responsibility to remove (or delete) all such static or automatic child widgets before destroying the group - otherwise the child widgets' destructors would be called twice! */ void add(Fl_Widget* w); 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 m_widgets; }; } // namespace v } // namespace giada #endif giada-0.22.0/src/gui/elems/basics/input.cpp000066400000000000000000000037211425106661500204400ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "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) { //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(p))->cb_onChange(); } /* -------------------------------------------------------------------------- */ void geInput::cb_onChange() { if (onChange != nullptr) onChange(value()); }giada-0.22.0/src/gui/elems/basics/input.h000066400000000000000000000027411425106661500201060ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_INPUT_H #define GE_INPUT_H #include #include #include class geInput : public Fl_Input { public: geInput(int x, int y, int w, int h, const char* l = 0); std::function onChange = nullptr; private: static void cb_onChange(Fl_Widget* w, void* p); void cb_onChange(); }; #endif giada-0.22.0/src/gui/elems/basics/liquidScroll.cpp000066400000000000000000000046161425106661500217530ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * gLiquidScroll * custom scroll that tells children to follow scroll's width when * resized. Thanks to Greg Ercolano from FLTK dev team. * http://seriss.com/people/erco/fltk/ * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "liquidScroll.h" #include "boxtypes.h" #include "core/const.h" namespace giada::v { geLiquidScroll::geLiquidScroll(int x, int y, int w, int h, Direction d) : geScroll(x, y, w, h, d == Direction::VERTICAL ? Fl_Scroll::VERTICAL_ALWAYS : Fl_Scroll::HORIZONTAL_ALWAYS) , m_direction(d) { } /* -------------------------------------------------------------------------- */ geLiquidScroll::geLiquidScroll(geompp::Rect r, Direction d) : geLiquidScroll(r.x, r.y, r.w, r.h, d) { } /* -------------------------------------------------------------------------- */ void geLiquidScroll::resize(int X, int Y, int W, int H) { const 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); if (m_direction == Direction::VERTICAL) c->resize(c->x(), c->y(), W - 24, c->h()); // -24: leave room for scrollbar else c->resize(c->x(), c->y(), c->w(), H - 24); // -24: leave room for scrollbar } init_sizes(); // tell scroll children changed in size Fl_Scroll::resize(X, Y, W, H); } } // namespace giada::vgiada-0.22.0/src/gui/elems/basics/liquidScroll.h000066400000000000000000000041451425106661500214150ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * gLiquidScroll * custom scroll that tells children to follow scroll's width when * resized. Thanks to Greg Ercolano from FLTK dev team. * http://seriss.com/people/erco/fltk/ * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_LIQUID_SCROLL_H #define GE_LIQUID_SCROLL_H #include "core/const.h" #include "deps/geompp/src/rect.hpp" #include "gui/types.h" #include "scroll.h" namespace giada::v { class geLiquidScroll : public geScroll { public: geLiquidScroll(int x, int y, int w, int h, Direction d); geLiquidScroll(geompp::Rect, Direction d); void resize(int x, int y, int w, int h) override; /* addWidget Adds a new widget to the bottom, with proper spacing. */ template T* addWidget(T* wg) { int numChildren = countChildren(); int wx = x(); int wy = y() - yposition() + (numChildren * (wg->h() + G_GUI_INNER_MARGIN)); int ww = w() - 24; int wh = wg->h(); wg->resize(wx, wy, ww, wh); add(wg); redraw(); return wg; } private: Direction m_direction; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/basics/pack.cpp000066400000000000000000000034631425106661500202220ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * geScroll * Custom scroll with nice scrollbars and something else. * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "pack.h" #include "core/const.h" namespace giada::v { gePack::gePack(int x, int y, Direction d, int gutter) : geGroup(x, y) , m_direction(d) , m_gutter(gutter) { 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); } } // namespace giada::vgiada-0.22.0/src/gui/elems/basics/pack.h000066400000000000000000000040611425106661500176620ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * geScroll * Custom scroll with nice scrollbars and something else. * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_PACK_H #define GE_PACK_H #include "core/const.h" #include "gui/elems/basics/group.h" #include "gui/types.h" namespace giada::v { /* 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); /* add Adds a Fl_Widget 'w' to this pack. Coordinates are relative to the group, so origin starts at (0, 0). As with any other FLTK group, the widget becomes owned by this group: If you add static or automatic (local) variables, then it is your responsibility to remove (or delete) all such static or automatic child widgets before destroying the group - otherwise the child widgets' destructors would be called twice! */ void add(Fl_Widget* w); private: Direction m_direction; int m_gutter; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/basics/progress.cpp000066400000000000000000000026561425106661500211530ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/basics/progress.h" #include "core/const.h" #include "gui/elems/basics/boxtypes.h" namespace giada::v { 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); } } // namespace giada::vgiada-0.22.0/src/gui/elems/basics/progress.h000066400000000000000000000025431425106661500206130ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_PROGRESS_H #define GE_PROGRESS_H #include namespace giada::v { class geProgress : public Fl_Progress { public: geProgress(int x, int y, int w, int h, const char* l = 0); }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/basics/resizerBar.cpp000066400000000000000000000151731425106661500214150ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "resizerBar.h" #include "core/const.h" #include #include #include #include #include #include namespace giada::v { geResizerBar::geResizerBar(int X, int Y, int W, int H, int minSize, Direction dir, Mode mode) : Fl_Box(X, Y, W, H) , m_direction(dir) , m_mode(mode) , m_minSize(minSize) , m_lastPos(0) , m_hover(false) { visible_focus(0); } /* -------------------------------------------------------------------------- */ void geResizerBar::handleDrag(int diff) { m_mode == Mode::MOVE ? move(diff) : resize(diff); Fl_Group* group = static_cast(parent()); group->init_sizes(); group->redraw(); } /* -------------------------------------------------------------------------- */ void geResizerBar::move(int diff) { Fl_Widget& wfirst = getFirstWidget(); std::vector wothers = findWidgets([this](const Fl_Widget& wd) { return isAfter(wd); }); if (m_direction == Direction::VERTICAL) { if (wfirst.h() + diff < m_minSize) diff = 0; wfirst.resize(wfirst.x(), wfirst.y(), wfirst.w(), wfirst.h() + diff); for (Fl_Widget* wd : wothers) wd->resize(wd->x(), wd->y() + diff, wd->w(), wd->h()); resize(x(), y() + diff, w(), h()); } else if (m_direction == Direction::HORIZONTAL) { if (wfirst.w() + diff < m_minSize) diff = 0; wfirst.resize(wfirst.x(), wfirst.y(), wfirst.w() + diff, wfirst.h()); for (Fl_Widget* wd : wothers) wd->resize(wd->x() + diff, wd->y(), wd->w(), wd->h()); resize(x() + diff, y(), w(), h()); } } /* -------------------------------------------------------------------------- */ void geResizerBar::resize(int diff) { Fl_Widget& wa = getFirstWidget(); Fl_Widget& wb = *findWidgets([this](const Fl_Widget& wd) { return isAfter(wd); }, /*howmany=*/1)[0]; if (m_direction == Direction::VERTICAL) { if (wa.h() + diff < m_minSize || wb.h() - diff < m_minSize) diff = 0; wa.resize(wa.x(), wa.y(), wa.w(), wa.h() + diff); wb.resize(wb.x(), wb.y() + diff, wb.w(), wb.h() - diff); resize(x(), y() + diff, w(), h()); } else if (m_direction == Direction::HORIZONTAL) { if (wa.w() + diff < m_minSize || wb.w() - diff < m_minSize) diff = 0; wa.resize(wa.x(), wa.y(), wa.w() + diff, wa.h()); wb.resize(wb.x() + diff, wb.y(), wb.w() - diff, wb.h()); resize(x() + diff, y(), w(), h()); } } /* -------------------------------------------------------------------------- */ bool geResizerBar::isBefore(const Fl_Widget& wd) const { const int before = m_direction == Direction::VERTICAL ? y() : x(); return (m_direction == Direction::VERTICAL && wd.y() + wd.h() == before) || (m_direction == Direction::HORIZONTAL && wd.x() + wd.w() == before); } /* -------------------------------------------------------------------------- */ bool geResizerBar::isAfter(const Fl_Widget& wd) const { const int after = m_direction == Direction::VERTICAL ? y() + h() : x() + w(); return (m_direction == Direction::VERTICAL && wd.y() >= after) || (m_direction == Direction::HORIZONTAL && wd.x() >= after); } /* -------------------------------------------------------------------------- */ Fl_Widget& geResizerBar::getFirstWidget() { return *findWidgets([this](const Fl_Widget& wd) { return isBefore(wd); }, /*howmany=*/1)[0]; } /* -------------------------------------------------------------------------- */ std::vector geResizerBar::findWidgets(std::function f, int howmany) const { std::vector out; Fl_Group* group = static_cast(parent()); for (int t = 0; t < group->children(); t++) { Fl_Widget* wd = group->child(t); if (!f(*wd)) continue; out.push_back(wd); if (howmany != -1 && out.size() == (size_t)howmany) break; } /* Make sure it finds the exact number of widgets requested, in case howmany != -1. */ assert(howmany == -1 || (howmany != -1 && out.size() == (size_t)howmany)); return out; } /* -------------------------------------------------------------------------- */ 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 currentPos = m_direction == Direction::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_direction == Direction::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; break; case FL_DRAG: handleDrag(currentPos - m_lastPos); m_lastPos = currentPos; ret = 1; if (onDrag != nullptr) onDrag(getFirstWidget()); break; case FL_RELEASE: if (onRelease != nullptr) onRelease(getFirstWidget()); break; default: break; } return (Fl_Box::handle(e) | ret); } /* -------------------------------------------------------------------------- */ void geResizerBar::resize(int X, int Y, int W, int H) { if (m_direction == Direction::VERTICAL) Fl_Box::resize(X, Y, W, h()); else Fl_Box::resize(X, Y, w(), H); } /* -------------------------------------------------------------------------- */ void geResizerBar::moveTo(int p) { const Fl_Widget& wd = getFirstWidget(); const int curr = m_direction == Direction::VERTICAL ? wd.h() : wd.w(); handleDrag(p - curr); } } // namespace giada::v giada-0.22.0/src/gui/elems/basics/resizerBar.h000066400000000000000000000064111425106661500210550ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_RESIZER_BAR_H #define GE_RESIZER_BAR_H #include #include /* geResizerBar A 'resizer bar' between widgets inside a Fl_Scroll. Thanks to Greg Ercolano from FLTK dev team (http://seriss.com/people/erco/fltk/). It also shows a resize cursor when hovered over. Assumes: - Parent is an Fl_Group; - The widget before us has an edge touching our edge; ie. w->y() + w->h() == this->y() if Direction::VERTICAL. When this widget is dragged: - The widget before us (with a common edge) will be resized; - if Mode == MOVE All children after us will be moved. - else if Mode == RESIZE The child after us is resized. */ namespace giada::v { class geResizerBar : public Fl_Box { public: enum class Direction { HORIZONTAL, VERTICAL }; enum class Mode { MOVE, RESIZE }; geResizerBar(int x, int y, int w, int h, int minSize, Direction dir, Mode m = Mode::MOVE); int handle(int e) override; void draw() override; void resize(int x, int y, int w, int h) override; void moveTo(int p); std::function onDrag = nullptr; std::function onRelease = nullptr; private: /* isBefore True if widget 'w' is before the drag bar. */ bool isBefore(const Fl_Widget& w) const; /* isBefore True if widget 'w' is after the drag bar. */ bool isAfter(const Fl_Widget& w) const; /* findWidgets Returns a vector of widgets according to a certain logic specified in the lambda function. Limits the output to 'howmany' widgets if 'howmany' != -1. */ std::vector findWidgets(std::function f, int howmany = -1) const; /* handleDrag Main entrypoint for the dragging operation. */ void handleDrag(int diff); /* move Resize the first widget and shift all others. */ void move(int diff); /* resize Resize the first and the second widget, leaving all others untouched. */ void resize(int diff); /* getFirstWidget Returns a ref to the first widget before the drag bar. */ Fl_Widget& getFirstWidget(); Direction m_direction; Mode m_mode; int m_minSize; int m_lastPos; bool m_hover; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/basics/scroll.cpp000066400000000000000000000057461425106661500206100ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * geScroll * Custom scroll with nice scrollbars and something else. * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "scroll.h" #include "boxtypes.h" #include "core/const.h" #include geScroll::geScroll(int x, int y, int w, int h, int t) : Fl_Scroll(x, y, w, h) { end(); type(t); scrollbar.color(G_COLOR_GREY_2); scrollbar.selection_color(G_COLOR_GREY_4); scrollbar.labelcolor(G_COLOR_LIGHT_1); scrollbar.slider(G_CUSTOM_BORDER_BOX); scrollbar.callback(cb_onScrollV, this); hscrollbar.color(G_COLOR_GREY_2); hscrollbar.selection_color(G_COLOR_GREY_4); hscrollbar.labelcolor(G_COLOR_LIGHT_1); hscrollbar.slider(G_CUSTOM_BORDER_BOX); hscrollbar.callback(cb_onScrollH, this); } /* -------------------------------------------------------------------------- */ geScroll::geScroll(int type) : geScroll(0, 0, 0, 0, type) { } /* -------------------------------------------------------------------------- */ void geScroll::cb_onScrollV(Fl_Widget* w, void* p) { geScroll* s = static_cast(w->parent()); Fl_Scrollbar* b = static_cast(w); s->scroll_to(s->xposition(), b->value()); (static_cast(p))->cb_onScrollV(); } void geScroll::cb_onScrollH(Fl_Widget* w, void* p) { geScroll* s = static_cast(w->parent()); Fl_Scrollbar* b = static_cast(w); s->scroll_to(b->value(), s->yposition()); (static_cast(p))->cb_onScrollH(); } /* -------------------------------------------------------------------------- */ void geScroll::cb_onScrollV() { if (onScrollV != nullptr) onScrollV(yposition()); } /* -------------------------------------------------------------------------- */ void geScroll::cb_onScrollH() { if (onScrollH != nullptr) onScrollH(xposition()); } /* -------------------------------------------------------------------------- */ int geScroll::countChildren() const { return children() - 2; // Exclude scrollbars }giada-0.22.0/src/gui/elems/basics/scroll.h000066400000000000000000000033331425106661500202430ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * geScroll * Custom scroll with nice scrollbars and something else. * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_SCROLL_H #define GE_SCROLL_H #include #include class geScroll : public Fl_Scroll { public: geScroll(int x, int y, int w, int h, int type = Fl_Scroll::BOTH); geScroll(int type = Fl_Scroll::BOTH); int countChildren() const; std::function onScrollV{nullptr}; std::function onScrollH{nullptr}; private: static void cb_onScrollV(Fl_Widget* w, void* p); static void cb_onScrollH(Fl_Widget* w, void* p); void cb_onScrollV(); void cb_onScrollH(); }; #endif giada-0.22.0/src/gui/elems/basics/scrollPack.cpp000066400000000000000000000046671425106661500214100ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "scrollPack.h" #include "boxtypes.h" #include "core/const.h" #include #include 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) , m_direction(dir) , m_gutter(gutter) { end(); } /* -------------------------------------------------------------------------- */ std::size_t geScrollPack::countChildren() const { 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); } /* -------------------------------------------------------------------------- */ Fl_Widget* geScrollPack::getChild(std::size_t i) { 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 } } // namespace v } // namespace giada giada-0.22.0/src/gui/elems/basics/scrollPack.h000066400000000000000000000040761425106661500210470ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_SCROLL_PACK_H #define GE_SCROLL_PACK_H #include "gui/elems/basics/pack.h" #include "gui/elems/basics/scroll.h" #include 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); /* 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(); 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 m_widgets; Direction m_direction; int m_gutter; }; } // namespace v } // namespace giada #endif giada-0.22.0/src/gui/elems/basics/slider.cpp000066400000000000000000000027471425106661500205720ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "slider.h" #include "../../../core/const.h" #include "boxtypes.h" 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); 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); } giada-0.22.0/src/gui/elems/basics/slider.h000066400000000000000000000024641425106661500202330ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_SLIDER_H #define GE_SLIDER_H #include class geSlider : public Fl_Slider { public: geSlider(int x, int y, int w, int h, const char* l = 0); int id; }; #endif giada-0.22.0/src/gui/elems/basics/split.cpp000066400000000000000000000041501425106661500204310ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "split.h" #include "core/const.h" #include "gui/elems/basics/box.h" namespace giada::v { geSplit::geSplit(int x, int y, int w, int h) : Fl_Group(x, y, w, h) , m_a(nullptr) , m_b(nullptr) , m_bar(0, 0, w, G_GUI_INNER_MARGIN, G_GUI_UNIT, geResizerBar::Direction::VERTICAL, geResizerBar::Mode::RESIZE) { end(); } /* -------------------------------------------------------------------------- */ void geSplit::init(Fl_Widget& a, Fl_Widget& b) { a.resize(x(), y(), w(), (h() / 2) - G_GUI_INNER_MARGIN); // Panel A goes on top a.redraw(); m_a = &a; m_bar.resize(x(), m_a->y() + m_a->h(), w(), G_GUI_INNER_MARGIN); b.resize(x(), m_bar.y() + m_bar.h(), w(), h() / 2); // Panel B goes on bottom b.redraw(); m_b = &b; Fl_Group::add(m_a); Fl_Group::add(m_bar); Fl_Group::add(m_b); resizable(m_a); } /* -------------------------------------------------------------------------- */ void geSplit::resizePanel(Panel p, int s) { m_bar.moveTo(p == Panel::A ? s : h() - s); } } // namespace giada::vgiada-0.22.0/src/gui/elems/basics/split.h000066400000000000000000000032221425106661500200750ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_SPLIT_H #define GE_SPLIT_H #include "gui/elems/basics/resizerBar.h" #include namespace giada::v { /* geSplit A resizable split-view widget that contains two horizontal panels (A and B). TODO - add vertical mode. */ class geSplit : public Fl_Group { public: enum class Panel { A, B }; geSplit(int x, int y, int w, int h); void init(Fl_Widget& a, Fl_Widget& b); void resizePanel(Panel p, int s); private: Fl_Widget* m_a; Fl_Widget* m_b; geResizerBar m_bar; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/basics/statusButton.cpp000066400000000000000000000044041425106661500220170ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * geStatusButton * Simple geButton with a boolean 'status' parameter. * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "statusButton.h" #include "core/const.h" #include 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) { } /* -------------------------------------------------------------------------- */ geStatusButton::geStatusButton(const char** imgOff, const char** imgOn, const char** imgDisabled) : geButton("", 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); else geButton::draw(imgDisabled, bgColor0, bdColor); } /* -------------------------------------------------------------------------- */ void geStatusButton::setStatus(bool s) { m_status = s; redraw(); } bool geStatusButton::getStatus() const { return m_status; } } // namespace giada::vgiada-0.22.0/src/gui/elems/basics/statusButton.h000066400000000000000000000033461425106661500214700ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * geStatusButton * Simple geButton with a boolean 'status' parameter. * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef 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); geStatusButton(const char** imgOff = nullptr, const char** imgOn = nullptr, const char** imgDisabled = nullptr); void draw() override; bool getStatus() const; void setStatus(bool s); private: bool m_status; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/basics/tabs.cpp000066400000000000000000000034021425106661500202260ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/basics/tabs.h" #include "core/const.h" #include "gui/elems/basics/boxtypes.h" namespace giada::v { geTabs::geTabs(geompp::Rect r) : Fl_Tabs(r.x, r.y, r.w, r.h) { box(G_CUSTOM_BORDER_BOX); labelcolor(G_COLOR_LIGHT_2); end(); } /* -------------------------------------------------------------------------- */ void geTabs::add(Fl_Widget* wg) { constexpr int TAB_HEIGHT = 25; wg->resize(x(), y() + TAB_HEIGHT, w(), h() - TAB_HEIGHT); wg->labelsize(G_GUI_FONT_SIZE_BASE); wg->selection_color(G_COLOR_GREY_4); Fl_Tabs::add(wg); resizable(wg);// To keep the tab height constant during resizing } } // namespace giada::v giada-0.22.0/src/gui/elems/basics/tabs.h000066400000000000000000000025521425106661500177000ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_TABS_H #define GE_TABS_H #include "deps/geompp/src/rect.hpp" #include namespace giada::v { class geTabs : public Fl_Tabs { public: geTabs(geompp::Rect); void add(Fl_Widget*); }; } // namespace giada::v #endifgiada-0.22.0/src/gui/elems/config/000077500000000000000000000000001425106661500165735ustar00rootroot00000000000000giada-0.22.0/src/gui/elems/config/stringMenu.cpp000066400000000000000000000031441425106661500214340ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/config/stringMenu.h" #include "utils/gui.h" namespace giada::v { geStringMenu::geStringMenu(const char* l, const std::vector& data, const std::string& msgIfNotFound, int labelWidth) : geChoice(l, labelWidth) { if (data.size() == 0) { addItem(msgIfNotFound.c_str(), 0); showItem(0); deactivate(); } else { for (const std::string& d : data) addItem(u::gui::removeFltkChars(d).c_str(), -1); // -1: auto-increment ID } } } // namespace giada::vgiada-0.22.0/src/gui/elems/config/stringMenu.h000066400000000000000000000027241425106661500211040ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_STRING_MENU_H #define GE_STRING_MENU_H #include "gui/elems/basics/choice.h" #include #include namespace giada::v { class geStringMenu : public geChoice { public: geStringMenu(const char* l, const std::vector& data, const std::string& msgIfNotFound, int labelWidth); }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/config/tabAudio.cpp000066400000000000000000000252751425106661500210420ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "tabAudio.h" #include "core/const.h" #include "core/kernelAudio.h" #include "deps/rtaudio/RtAudio.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/check.h" #include "gui/elems/basics/choice.h" #include "gui/elems/basics/flex.h" #include "gui/elems/basics/input.h" #include "gui/ui.h" #include "utils/string.h" #include constexpr int LABEL_WIDTH = 110; extern giada::v::Ui g_ui; namespace giada::v { geTabAudio::geDeviceMenu::geDeviceMenu(const char* l, const std::vector& devices) : geChoice(l, LABEL_WIDTH) { if (devices.size() == 0) { addItem(g_ui.langMapper.get(LangMap::CONFIG_AUDIO_NODEVICESFOUND), 0); showItem(0); return; } for (const c::config::AudioDeviceData& device : devices) addItem(device.name, device.index); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ geTabAudio::geChannelMenu::geChannelMenu(const char* l, const c::config::AudioDeviceData& data) : geChoice(l, LABEL_WIDTH) , m_data(data) { } /* -------------------------------------------------------------------------- */ int geTabAudio::geChannelMenu::getChannelsCount() const { return getSelectedId() < STEREO_OFFSET ? 1 : 2; } int geTabAudio::geChannelMenu::getChannelsStart() const { if (m_data.channelsCount == 1) return getSelectedId(); return getSelectedId() < STEREO_OFFSET ? getSelectedId() : getSelectedId() - STEREO_OFFSET; } /* -------------------------------------------------------------------------- */ void geTabAudio::geChannelMenu::rebuild(const c::config::AudioDeviceData& data) { m_data = data; clear(); if (m_data.index == -1) { addItem(g_ui.langMapper.get(LangMap::COMMON_NONE), 0); showItem(0); return; } if (m_data.type == c::config::DeviceType::INPUT) for (int i = 0; i < m_data.channelsMax; i++) addItem(std::to_string(i + 1), i); /* Dirty trick for stereo channels: they start at STEREO_OFFSET. */ for (int i = 0; i < m_data.channelsMax; i += 2) addItem(std::to_string(i + 1) + "-" + std::to_string(i + 2), i + STEREO_OFFSET); if (m_data.channelsCount == 1) showItem(m_data.channelsStart); else showItem(m_data.channelsStart + STEREO_OFFSET); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ geTabAudio::geTabAudio(geompp::Rect bounds) : Fl_Group(bounds.x, bounds.y, bounds.w, bounds.h, g_ui.langMapper.get(LangMap::CONFIG_AUDIO_TITLE)) , m_data(c::config::getAudioData()) , m_initialApi(m_data.api) { end(); geFlex* body = new geFlex(bounds.reduced(G_GUI_OUTER_MARGIN), Direction::VERTICAL, G_GUI_OUTER_MARGIN); { soundsys = new geChoice(g_ui.langMapper.get(LangMap::CONFIG_AUDIO_SYSTEM), LABEL_WIDTH); geFlex* line1 = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN); { buffersize = new geChoice(g_ui.langMapper.get(LangMap::CONFIG_AUDIO_BUFFERSIZE), LABEL_WIDTH); samplerate = new geChoice(g_ui.langMapper.get(LangMap::CONFIG_AUDIO_SAMPLERATE), LABEL_WIDTH); line1->add(buffersize, 180); line1->add(samplerate, 180); line1->end(); } sounddevOut = new geDeviceMenu(g_ui.langMapper.get(LangMap::CONFIG_AUDIO_OUTPUTDEVICE), m_data.outputDevices); geFlex* line2 = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN); { channelsOut = new geChannelMenu(g_ui.langMapper.get(LangMap::CONFIG_AUDIO_OUTPUTCHANNELS), m_data.outputDevice); limitOutput = new geCheck(x() + 177, y() + 93, 100, 20, g_ui.langMapper.get(LangMap::CONFIG_AUDIO_LIMITOUTPUT)); line2->add(channelsOut, 180); line2->add(limitOutput); line2->end(); } geFlex* line3 = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN); { sounddevIn = new geDeviceMenu(g_ui.langMapper.get(LangMap::CONFIG_AUDIO_INPUTDEVICE), m_data.inputDevices); enableIn = new geCheck(0, 0, 0, 0); line3->add(sounddevIn); line3->add(enableIn, 12); line3->end(); } geFlex* line4 = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN); { channelsIn = new geChannelMenu(g_ui.langMapper.get(LangMap::CONFIG_AUDIO_INPUTCHANNELS), m_data.inputDevice); recTriggerLevel = new geInput(0, 0, 0, 0, g_ui.langMapper.get(LangMap::CONFIG_AUDIO_RECTHRESHOLD)); line4->add(channelsIn, 180); line4->add(new geBox(), 132); // TODO - temporary hack for geInput's label line4->add(recTriggerLevel, 40); line4->end(); } rsmpQuality = new geChoice(g_ui.langMapper.get(LangMap::CONFIG_AUDIO_RESAMPLING), LABEL_WIDTH); body->add(soundsys, 20); body->add(line1, 20); body->add(sounddevOut, 20); body->add(line2, 20); body->add(line3, 20); body->add(line4, 20); body->add(rsmpQuality, 20); body->add(new geBox(g_ui.langMapper.get(LangMap::CONFIG_RESTARTGIADA))); body->end(); } add(body); resizable(body); 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(g_ui.langMapper.get(LangMap::CONFIG_AUDIO_ENABLEINPUT)); 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(g_ui.langMapper.get(LangMap::CONFIG_AUDIO_RESAMPLING_SINCBEST), 0); rsmpQuality->addItem(g_ui.langMapper.get(LangMap::CONFIG_AUDIO_RESAMPLING_SINCMEDIUM), 1); rsmpQuality->addItem(g_ui.langMapper.get(LangMap::CONFIG_AUDIO_RESAMPLING_SINCBASIC), 2); rsmpQuality->addItem(g_ui.langMapper.get(LangMap::CONFIG_AUDIO_RESAMPLING_ZEROORDER), 3); rsmpQuality->addItem(g_ui.langMapper.get(LangMap::CONFIG_AUDIO_RESAMPLING_LINEAR), 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(); } /* -------------------------------------------------------------------------- */ void geTabAudio::invalidate() { /* If the user changes sound system (e.g. ALSA->JACK), deactivate all widgets. */ if (m_initialApi == m_data.api && m_initialApi != -1 && m_data.api != G_SYS_API_NONE) activateAll(); else deactivateAll(); } /* -------------------------------------------------------------------------- */ void geTabAudio::fetch() { for (int sampleRate : m_data.outputDevice.sampleRates) samplerate->addItem(std::to_string(sampleRate), sampleRate); samplerate->showItem(m_data.sampleRate); channelsOut->rebuild(m_data.outputDevice); m_data.outputDevice.channelsCount = channelsOut->getChannelsCount(); m_data.outputDevice.channelsStart = channelsOut->getChannelsStart(); 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(); } else { sounddevIn->deactivate(); channelsIn->deactivate(); recTriggerLevel->deactivate(); } } /* -------------------------------------------------------------------------- */ void geTabAudio::deactivateAll() { buffersize->deactivate(); limitOutput->deactivate(); sounddevOut->deactivate(); channelsOut->deactivate(); samplerate->deactivate(); sounddevIn->deactivate(); channelsIn->deactivate(); recTriggerLevel->deactivate(); rsmpQuality->deactivate(); } /* -------------------------------------------------------------------------- */ void geTabAudio::activateAll() { 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() { c::config::save(m_data); } } // namespace giada::vgiada-0.22.0/src/gui/elems/config/tabAudio.h000066400000000000000000000045351425106661500205030ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_TAB_AUDIO_H #define GE_TAB_AUDIO_H #include "deps/geompp/src/rect.hpp" #include "glue/config.h" #include "gui/elems/basics/choice.h" #include class geCheck; class geInput; namespace giada::v { class geTabAudio : public Fl_Group { public: struct geDeviceMenu : public geChoice { geDeviceMenu(const char* l, const std::vector&); }; struct geChannelMenu : public geChoice { geChannelMenu(const char* l, const c::config::AudioDeviceData&); int getChannelsCount() const; int getChannelsStart() const; void rebuild(const c::config::AudioDeviceData&); private: static constexpr int STEREO_OFFSET = 1000; c::config::AudioDeviceData m_data; }; geTabAudio(geompp::Rect); void save(); 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(); c::config::AudioData m_data; int m_initialApi; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/config/tabBehaviors.cpp000066400000000000000000000060541425106661500217150ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "tabBehaviors.h" #include "core/conf.h" #include "core/const.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/check.h" #include "gui/elems/basics/flex.h" #include "gui/ui.h" #include extern giada::v::Ui g_ui; namespace giada::v { geTabBehaviors::geTabBehaviors(geompp::Rect bounds, m::Conf::Data& c) : Fl_Group(bounds.x, bounds.y, bounds.w, bounds.h, g_ui.langMapper.get(LangMap::CONFIG_BEHAVIORS_TITLE)) , m_conf(c) { end(); geFlex* body = new geFlex(bounds.reduced(G_GUI_OUTER_MARGIN), Direction::VERTICAL, G_GUI_OUTER_MARGIN); { m_chansStopOnSeqHalt = new geCheck(0, 0, 0, 0, g_ui.langMapper.get(LangMap::CONFIG_BEHAVIORS_CHANSSTOPONSEQHALT)); m_treatRecsAsLoops = new geCheck(0, 0, 0, 0, g_ui.langMapper.get(LangMap::CONFIG_BEHAVIORS_TREATRECSASLOOPS)); m_inputMonitorDefaultOn = new geCheck(0, 0, 0, 0, g_ui.langMapper.get(LangMap::CONFIG_BEHAVIORS_INPUTMONITORDEFAULTON)); m_overdubProtectionDefaultOn = new geCheck(0, 0, 0, 0, g_ui.langMapper.get(LangMap::CONFIG_BEHAVIORS_OVERDUBPROTECTIONDEFAULTON)); body->add(m_chansStopOnSeqHalt, 30); body->add(m_treatRecsAsLoops, 20); body->add(m_inputMonitorDefaultOn, 20); body->add(m_overdubProtectionDefaultOn, 30); body->end(); }; add(body); resizable(body); m_chansStopOnSeqHalt->value(m_conf.chansStopOnSeqHalt); m_treatRecsAsLoops->value(m_conf.treatRecsAsLoops); m_inputMonitorDefaultOn->value(m_conf.inputMonitorDefaultOn); m_overdubProtectionDefaultOn->value(m_conf.overdubProtectionDefaultOn); } /* -------------------------------------------------------------------------- */ void geTabBehaviors::save() { m_conf.chansStopOnSeqHalt = m_chansStopOnSeqHalt->value(); m_conf.treatRecsAsLoops = m_treatRecsAsLoops->value(); m_conf.inputMonitorDefaultOn = m_inputMonitorDefaultOn->value(); m_conf.overdubProtectionDefaultOn = m_overdubProtectionDefaultOn->value(); } } // namespace giada::vgiada-0.22.0/src/gui/elems/config/tabBehaviors.h000066400000000000000000000031751425106661500213630ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_TAB_BEHAVIORS_H #define GE_TAB_BEHAVIORS_H #include "core/conf.h" #include "deps/geompp/src/rect.hpp" #include "gui/elems/basics/check.h" #include namespace giada::v { class geTabBehaviors : public Fl_Group { public: geTabBehaviors(geompp::Rect, m::Conf::Data&); void save(); private: geCheck* m_chansStopOnSeqHalt; geCheck* m_treatRecsAsLoops; geCheck* m_inputMonitorDefaultOn; geCheck* m_overdubProtectionDefaultOn; m::Conf::Data& m_conf; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/config/tabBindings.cpp000066400000000000000000000050551425106661500215300ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/config/tabBindings.h" #include "core/const.h" #include "gui/elems/basics/liquidScroll.h" #include "gui/elems/keyBinder.h" #include "gui/ui.h" #include "utils/gui.h" extern giada::v::Ui g_ui; namespace giada::v { geTabBindings::geTabBindings(geompp::Rect bounds, m::Conf::Data& conf) : Fl_Group(bounds.x, bounds.y, bounds.w, bounds.h, g_ui.langMapper.get(LangMap::CONFIG_BINDINGS_TITLE)) { end(); geFlex* body = new geFlex(bounds.reduced(G_GUI_OUTER_MARGIN), Direction::VERTICAL, G_GUI_INNER_MARGIN); { play = new geKeyBinder(g_ui.langMapper.get(LangMap::CONFIG_BINDINGS_PLAY), conf.keyBindings[m::Conf::KEY_BIND_PLAY]); rewind = new geKeyBinder(g_ui.langMapper.get(LangMap::CONFIG_BINDINGS_REWIND), conf.keyBindings[m::Conf::KEY_BIND_REWIND]); recordActions = new geKeyBinder(g_ui.langMapper.get(LangMap::CONFIG_BINDINGS_RECORDACTIONS), conf.keyBindings[m::Conf::KEY_BIND_RECORD_ACTIONS]); recordInput = new geKeyBinder(g_ui.langMapper.get(LangMap::CONFIG_BINDINGS_RECORDAUDIO), conf.keyBindings[m::Conf::KEY_BIND_RECORD_INPUT]); exit = new geKeyBinder(g_ui.langMapper.get(LangMap::CONFIG_BINDINGS_EXIT), conf.keyBindings[m::Conf::KEY_BIND_EXIT]); body->add(play, G_GUI_UNIT); body->add(rewind, G_GUI_UNIT); body->add(recordActions, G_GUI_UNIT); body->add(recordInput, G_GUI_UNIT); body->add(exit, G_GUI_UNIT); body->end(); } add(body); resizable(body); } } // namespace giada::vgiada-0.22.0/src/gui/elems/config/tabBindings.h000066400000000000000000000031321425106661500211670ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_CONFIG_TAB_BINDINGS_H #define GE_CONFIG_TAB_BINDINGS_H #include "core/conf.h" #include "deps/geompp/src/rect.hpp" #include class geCheck; class geInput; namespace giada::v { class geKeyBinder; class geTabBindings : public Fl_Group { public: geTabBindings(geompp::Rect, m::Conf::Data&); private: geKeyBinder* play; geKeyBinder* rewind; geKeyBinder* recordActions; geKeyBinder* recordInput; geKeyBinder* exit; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/config/tabMidi.cpp000066400000000000000000000124701425106661500206540ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/config/tabMidi.h" #include "core/const.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/check.h" #include "gui/elems/basics/choice.h" #include "gui/elems/config/stringMenu.h" #include "gui/ui.h" #include "utils/gui.h" #include constexpr int LABEL_WIDTH = 120; extern giada::v::Ui g_ui; namespace giada::v { geTabMidi::geTabMidi(geompp::Rect bounds) : Fl_Group(bounds.x, bounds.y, bounds.w, bounds.h, g_ui.langMapper.get(LangMap::CONFIG_MIDI_TITLE)) , m_data(c::config::getMidiData()) , m_initialApi(m_data.api) { end(); geFlex* body = new geFlex(bounds.reduced(G_GUI_OUTER_MARGIN), Direction::VERTICAL, G_GUI_OUTER_MARGIN); { system = new geChoice(g_ui.langMapper.get(LangMap::CONFIG_MIDI_SYSTEM), LABEL_WIDTH); geFlex* line1 = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN); { portOut = new geStringMenu(g_ui.langMapper.get(LangMap::CONFIG_MIDI_OUTPUTPORT), m_data.outPorts, g_ui.langMapper.get(LangMap::CONFIG_MIDI_NOPORTSFOUND), LABEL_WIDTH); enableOut = new geCheck(0, 0, 0, 0); line1->add(portOut); line1->add(enableOut, 12); line1->end(); } geFlex* line2 = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN); { portIn = new geStringMenu(g_ui.langMapper.get(LangMap::CONFIG_MIDI_INPUTPORT), m_data.inPorts, g_ui.langMapper.get(LangMap::CONFIG_MIDI_NOPORTSFOUND), LABEL_WIDTH); enableIn = new geCheck(0, 0, 0, 0); line2->add(portIn); line2->add(enableIn, 12); line2->end(); } midiMap = new geStringMenu(g_ui.langMapper.get(LangMap::CONFIG_MIDI_OUTPUTMIDIMAP), m_data.midiMaps, g_ui.langMapper.get(LangMap::CONFIG_MIDI_NOMIDIMAPSFOUND), LABEL_WIDTH); sync = new geChoice(g_ui.langMapper.get(LangMap::CONFIG_MIDI_SYNC), LABEL_WIDTH); body->add(system, 20); body->add(line1, 20); body->add(line2, 20); body->add(midiMap, 20); body->add(sync, 20); body->add(new geBox(g_ui.langMapper.get(LangMap::CONFIG_RESTARTGIADA))); body->end(); } add(body); resizable(body); for (const auto& [key, value] : m_data.apis) system->addItem(value.c_str(), key); system->showItem(m_data.api); system->onChange = [this](ID id) { m_data.api = id; invalidate(); }; portOut->showItem(m_data.outPort); portOut->onChange = [this](ID id) { m_data.outPort = id; }; if (m_data.outPort == -1) portOut->deactivate(); portIn->showItem(m_data.inPort); portIn->onChange = [this](ID id) { m_data.inPort = id; }; if (m_data.inPort == -1) portIn->deactivate(); enableOut->copy_tooltip(g_ui.langMapper.get(LangMap::CONFIG_MIDI_LABEL_ENABLEOUT)); enableOut->value(m_data.outPort != -1); enableOut->onChange = [this](bool b) { if (b) { m_data.outPort = portOut->getSelectedId(); portOut->activate(); } else { m_data.outPort = -1; portOut->deactivate(); } }; enableIn->copy_tooltip(g_ui.langMapper.get(LangMap::CONFIG_MIDI_LABEL_ENABLEIN)); enableIn->value(m_data.inPort != -1); enableIn->onChange = [this](bool b) { if (b) { m_data.inPort = portIn->getSelectedId(); portIn->activate(); } else { m_data.inPort = -1; portIn->deactivate(); } }; midiMap->showItem(m_data.midiMap); midiMap->onChange = [this](ID id) { m_data.midiMap = id; }; for (const auto& [key, value] : m_data.syncModes) sync->addItem(value.c_str(), key); sync->showItem(m_data.syncMode); sync->onChange = [this](ID id) { m_data.syncMode = id; }; } /* -------------------------------------------------------------------------- */ void geTabMidi::invalidate() { /* 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 re-using previous data. */ if (m_initialApi == m_data.api && m_initialApi != -1) { portOut->activate(); portIn->activate(); enableOut->activate(); enableIn->activate(); if (m_data.midiMaps.size() > 0) midiMap->activate(); sync->activate(); } else { portOut->deactivate(); portIn->deactivate(); enableOut->deactivate(); enableIn->deactivate(); midiMap->deactivate(); sync->deactivate(); } } /* -------------------------------------------------------------------------- */ void geTabMidi::save() const { c::config::save(m_data); } } // namespace giada::v giada-0.22.0/src/gui/elems/config/tabMidi.h000066400000000000000000000032741425106661500203230ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_TAB_MIDI_H #define GE_TAB_MIDI_H #include "deps/geompp/src/rect.hpp" #include "glue/config.h" #include class geCheck; namespace giada::v { class geStringMenu; class geChoice; class geTabMidi : public Fl_Group { public: geTabMidi(geompp::Rect); void save() const; geChoice* system; geStringMenu* portOut; geStringMenu* portIn; geCheck* enableOut; geCheck* enableIn; geStringMenu* midiMap; geChoice* sync; private: void invalidate(); c::config::MidiData m_data; int m_initialApi; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/config/tabMisc.cpp000066400000000000000000000062311425106661500206630ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "tabMisc.h" #include "core/const.h" #include "gui/elems/basics/choice.h" #include "gui/elems/config/stringMenu.h" #include "gui/ui.h" constexpr int LABEL_WIDTH = 120; extern giada::v::Ui g_ui; namespace giada::v { geTabMisc::geTabMisc(geompp::Rect bounds) : Fl_Group(bounds.x, bounds.y, bounds.w, bounds.h, g_ui.langMapper.get(LangMap::CONFIG_MISC_TITLE)) , m_data(c::config::getMiscData()) { end(); geFlex* body = new geFlex(bounds.reduced(G_GUI_OUTER_MARGIN), Direction::VERTICAL, G_GUI_OUTER_MARGIN); { m_debugMsg = new geChoice(g_ui.langMapper.get(LangMap::CONFIG_MISC_DEBUGMESSAGES), LABEL_WIDTH); m_tooltips = new geChoice(g_ui.langMapper.get(LangMap::CONFIG_MISC_TOOLTIPS), LABEL_WIDTH); m_langMap = new geStringMenu(g_ui.langMapper.get(LangMap::CONFIG_MISC_LANGUAGE), m_data.langMaps, g_ui.langMapper.get(LangMap::CONFIG_MISC_NOLANGUAGESFOUND), LABEL_WIDTH); body->add(m_debugMsg, 20); body->add(m_tooltips, 20); body->add(m_langMap, 20); body->end(); } add(body); resizable(body); m_debugMsg->addItem(g_ui.langMapper.get(LangMap::CONFIG_MISC_DEBUGMESSAGES_DISABLED)); m_debugMsg->addItem(g_ui.langMapper.get(LangMap::CONFIG_MISC_DEBUGMESSAGES_TOSTDOUT)); m_debugMsg->addItem(g_ui.langMapper.get(LangMap::CONFIG_MISC_DEBUGMESSAGES_TOFILE)); m_debugMsg->showItem(m_data.logMode); m_debugMsg->onChange = [this](ID id) { m_data.logMode = id; }; m_tooltips->addItem(g_ui.langMapper.get(LangMap::CONFIG_MISC_TOOLTIPS_DISABLED)); m_tooltips->addItem(g_ui.langMapper.get(LangMap::CONFIG_MISC_TOOLTIPS_ENABLED)); m_tooltips->showItem(m_data.showTooltips); m_tooltips->onChange = [this](ID id) { m_data.showTooltips = id; }; m_langMap->addItem("English (default)"); if (m_data.langMap == "") m_langMap->showItem(0); else m_langMap->showItem(m_data.langMap); m_langMap->onChange = [this](ID /*id*/) { m_data.langMap = m_langMap->getSelectedLabel(); }; } /* -------------------------------------------------------------------------- */ void geTabMisc::save() { c::config::save(m_data); } } // namespace giada::vgiada-0.22.0/src/gui/elems/config/tabMisc.h000066400000000000000000000030471425106661500203320ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_TAB_MISC_H #define GE_TAB_MISC_H #include "deps/geompp/src/rect.hpp" #include "glue/config.h" #include namespace giada::v { class geChoice; class geStringMenu; class geTabMisc : public Fl_Group { public: geTabMisc(geompp::Rect); void save(); private: c::config::MiscData m_data; geChoice* m_debugMsg; geChoice* m_tooltips; geStringMenu* m_langMap; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/config/tabPlugins.cpp000066400000000000000000000073041425106661500214130ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #include "tabPlugins.h" #include "core/conf.h" #include "core/const.h" #include "core/graphics.h" #include "glue/layout.h" #include "glue/plugin.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/flex.h" #include "gui/elems/basics/input.h" #include "gui/ui.h" #include "utils/gui.h" #include "utils/string.h" #include #include #include extern giada::v::Ui g_ui; namespace giada::v { geTabPlugins::geTabPlugins(geompp::Rect bounds) : Fl_Group(bounds.x, bounds.y, bounds.w, bounds.h, g_ui.langMapper.get(LangMap::CONFIG_PLUGINS_TITLE)) { end(); geFlex* body = new geFlex(bounds.reduced(G_GUI_OUTER_MARGIN), Direction::VERTICAL, G_GUI_OUTER_MARGIN); { geFlex* line1 = new geFlex(Direction::HORIZONTAL, G_GUI_OUTER_MARGIN); { m_folderPath = new geInput(0, 0, 0, 0); m_browse = new geButton("", zoomInOff_xpm, zoomInOn_xpm); line1->add(new geBox(), 80); // TODO - temporary hack for geInput's label line1->add(m_folderPath); line1->add(m_browse, 20); line1->end(); } m_scanButton = new geButton(); m_info = new geBox(); body->add(line1, 20); body->add(m_scanButton, 20); body->add(m_info); body->end(); } add(body); resizable(body); m_info->hide(); m_folderPath->label(g_ui.langMapper.get(LangMap::CONFIG_PLUGINS_FOLDER)); m_folderPath->onChange = [this](const std::string& v) { m_data.pluginPath = v; }; m_browse->onClick = [this]() { c::layout::openBrowserForPlugins(*static_cast(top_window())); }; m_scanButton->onClick = [this]() { std::function callback = [this](float progress) { std::string l = fmt::format(g_ui.langMapper.get(LangMap::CONFIG_PLUGINS_SCANNING), static_cast(progress * 100)); m_info->label(l.c_str()); Fl::wait(); }; m_info->show(); c::config::scanPlugins(m_folderPath->value(), callback); m_info->hide(); rebuild(); }; rebuild(); } /* -------------------------------------------------------------------------- */ void geTabPlugins::rebuild() { m_data = c::config::getPluginData(); const std::string scanLabel = fmt::format(g_ui.langMapper.get(LangMap::CONFIG_PLUGINS_SCAN), m_data.numAvailablePlugins); m_scanButton->copy_label(scanLabel.c_str()); m_folderPath->value(m_data.pluginPath.c_str()); m_folderPath->redraw(); } /* -------------------------------------------------------------------------- */ void geTabPlugins::save() { c::config::save(m_data); } } // namespace giada::v #endif // WITH_VSTgiada-0.22.0/src/gui/elems/config/tabPlugins.h000066400000000000000000000032131425106661500210530ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_TAB_PLUGINS_H #define GE_TAB_PLUGINS_H #ifdef WITH_VST #include "deps/geompp/src/rect.hpp" #include "glue/config.h" #include class geInput; class geBox; namespace giada::v { class geBox; class geButton; class geTabPlugins : public Fl_Group { public: geTabPlugins(geompp::Rect); void save(); void rebuild(); private: c::config::PluginData m_data; geButton* m_browse; geInput* m_folderPath; geButton* m_scanButton; geBox* m_info; }; } // namespace giada::v #endif // WITH_VST #endif giada-0.22.0/src/gui/elems/fileBrowser.cpp000066400000000000000000000105451425106661500203220ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "fileBrowser.h" #include "basics/boxtypes.h" #include "core/const.h" #include "gui/dialogs/browser/browserBase.h" #include "utils/fs.h" #include "utils/string.h" namespace giada::v { geFileBrowser::geFileBrowser(int x, int y, int w, int h) : Fl_File_Browser(x, y, w, h) , m_showHiddenFiles(false) { box(G_CUSTOM_BORDER_BOX); textsize(G_GUI_FONT_SIZE_BASE); textcolor(G_COLOR_LIGHT_2); selection_color(G_COLOR_GREY_4); color(G_COLOR_GREY_2); type(FL_SELECT_BROWSER); this->scrollbar.color(G_COLOR_GREY_2); this->scrollbar.selection_color(G_COLOR_GREY_4); this->scrollbar.labelcolor(G_COLOR_LIGHT_1); this->scrollbar.slider(G_CUSTOM_BORDER_BOX); this->hscrollbar.color(G_COLOR_GREY_2); this->hscrollbar.selection_color(G_COLOR_GREY_4); this->hscrollbar.labelcolor(G_COLOR_LIGHT_1); this->hscrollbar.slider(G_CUSTOM_BORDER_BOX); take_focus(); // let it have focus on startup } /* -------------------------------------------------------------------------- */ void geFileBrowser::toggleHiddenFiles() { m_showHiddenFiles = !m_showHiddenFiles; loadDir(m_currentDir); } /* -------------------------------------------------------------------------- */ void geFileBrowser::loadDir(const std::string& dir) { m_currentDir = dir; load(m_currentDir.c_str()); /* Clean up unwanted elements. Hide "../" first, it just screws up things. Also remove hidden files, if requested. */ for (int i = size(); i >= 0; i--) { if (text(i) == nullptr) continue; if (strcmp(text(i), "../") == 0 || (!m_showHiddenFiles && strncmp(text(i), ".", 1) == 0)) remove(i); } } /* -------------------------------------------------------------------------- */ int geFileBrowser::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(parent())->fireCallback(); ret = 1; break; case FL_PUSH: // mouse if (Fl::event_clicks() > 0) // double click static_cast(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 geFileBrowser::getCurrentDir() { return u::fs::getRealPath(m_currentDir); } /* -------------------------------------------------------------------------- */ std::string geFileBrowser::getSelectedItem(bool fullPath) { if (!fullPath) // no full path requested? return the selected text return text(value()); else if (value() == 0) // no rows selected? return current directory return m_currentDir; else return u::fs::getRealPath(u::fs::join(m_currentDir, text(value()))); } /* -------------------------------------------------------------------------- */ void geFileBrowser::preselect(int pos, int line) { position(pos); select(line); } } // namespace giada::vgiada-0.22.0/src/gui/elems/fileBrowser.h000066400000000000000000000035621425106661500177700ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ge_browser * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_FILE_BROWSER_H #define GE_FILE_BROWSER_H #include #include namespace giada::v { class geFileBrowser : public Fl_File_Browser { public: geFileBrowser(int x, int y, int w, int h); void toggleHiddenFiles(); /* init Initializes browser and show 'dir' as initial directory. */ void loadDir(const std::string& dir); /* getSelectedItem 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 getCurrentDir(); void preselect(int position, int line); int handle(int e); private: std::string m_currentDir; bool m_showHiddenFiles; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/keyBinder.cpp000066400000000000000000000044641425106661500177560ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/keyBinder.h" #include "core/const.h" #include "glue/layout.h" #include "gui/dialogs/keyGrabber.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/boxtypes.h" #include "gui/elems/basics/button.h" #include "gui/ui.h" #include "utils/gui.h" extern giada::v::Ui g_ui; namespace giada::v { geKeyBinder::geKeyBinder(const std::string& l, int& keyRef) : geFlex(Direction::HORIZONTAL, G_GUI_INNER_MARGIN) { m_labelBox = new geBox(l.c_str()); m_keyBox = new geBox(u::gui::keyToString(keyRef).c_str()); m_bindBtn = new geButton(g_ui.langMapper.get(LangMap::COMMON_BIND)); m_clearBtn = new geButton(g_ui.langMapper.get(LangMap::COMMON_CLEAR)); add(m_labelBox); add(m_keyBox, 100); add(m_bindBtn, 50); add(m_clearBtn, 50); end(); m_labelBox->box(G_CUSTOM_BORDER_BOX); m_keyBox->box(G_CUSTOM_BORDER_BOX); m_bindBtn->onClick = [&keyRef, this]() { c::layout::openKeyGrabberWindow(keyRef, [&keyRef, this](int newKey) { keyRef = newKey; m_keyBox->copy_label(u::gui::keyToString(keyRef).c_str()); return true; }); }; m_clearBtn->onClick = [&keyRef, this]() { keyRef = 0; m_keyBox->copy_label(u::gui::keyToString(keyRef).c_str()); }; } } // namespace giada::vgiada-0.22.0/src/gui/elems/keyBinder.h000066400000000000000000000027611425106661500174210ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_KEY_BINDER_H #define GE_KEY_BINDER_H #include "gui/elems/basics/flex.h" #include namespace giada::v { class geBox; class geButton; class geKeyBinder : public geFlex { public: geKeyBinder(const std::string& l, int& keyRef); private: geBox* m_labelBox; geBox* m_keyBox; geButton* m_bindBtn; geButton* m_clearBtn; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/mainWindow/000077500000000000000000000000001425106661500174425ustar00rootroot00000000000000giada-0.22.0/src/gui/elems/mainWindow/keyboard/000077500000000000000000000000001425106661500212425ustar00rootroot00000000000000giada-0.22.0/src/gui/elems/mainWindow/keyboard/channel.cpp000066400000000000000000000142741425106661500233660ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "glue/channel.h" #include "core/graphics.h" #include "glue/events.h" #include "glue/layout.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/dial.h" #include "gui/elems/basics/statusButton.h" #include "gui/elems/mainWindow/keyboard/channel.h" #include "gui/elems/mainWindow/keyboard/channelButton.h" #include "gui/elems/mainWindow/keyboard/channelStatus.h" #include "gui/elems/mainWindow/keyboard/column.h" #include "gui/elems/mainWindow/keyboard/midiActivity.h" #include "gui/ui.h" #include #include extern giada::v::Ui g_ui; namespace giada::v { geChannel::geChannel(int X, int Y, int W, int H, c::channel::Data d) : Fl_Group(X, Y, W, H) , m_channel(d) { } /* -------------------------------------------------------------------------- */ void geChannel::draw() { const int ny = y() + (h() / 2) - (G_GUI_UNIT / 2); playButton->resize(playButton->x(), ny, G_GUI_UNIT, G_GUI_UNIT); arm->resize(arm->x(), ny, G_GUI_UNIT, G_GUI_UNIT); mute->resize(mute->x(), ny, G_GUI_UNIT, G_GUI_UNIT); solo->resize(solo->x(), ny, G_GUI_UNIT, G_GUI_UNIT); vol->resize(vol->x(), ny, G_GUI_UNIT, G_GUI_UNIT); #ifdef WITH_VST fx->resize(fx->x(), ny, G_GUI_UNIT, G_GUI_UNIT); #endif fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_1_5); 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(); } #endif /* -------------------------------------------------------------------------- */ void geChannel::refresh() { ChannelStatus playStatus = m_channel.getPlayStatus(); ChannelStatus recStatus = m_channel.getRecStatus(); if (mainButton->visible()) mainButton->refresh(); if (recStatus == ChannelStatus::WAIT || playStatus == ChannelStatus::WAIT) blink(); playButton->setStatus(playStatus == ChannelStatus::PLAY || playStatus == ChannelStatus::ENDING); midiActivity->redraw(); 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() { c::layout::openChannelPluginListWindow(m_channel.id); } #endif /* -------------------------------------------------------------------------- */ int geChannel::getColumnId() const { return static_cast(parent())->id; } /* -------------------------------------------------------------------------- */ void geChannel::blink() { if (g_ui.shouldBlink()) mainButton->setPlayMode(); else mainButton->setDefaultMode(); } /* -------------------------------------------------------------------------- */ void geChannel::packWidgets() { /* Compute how much space is visible for the main button, then resize it according to that amount. */ int visible = w(); for (int i = 0; i < children(); i++) { if (child(i)->visible() && child(i) != mainButton) visible -= child(i)->w() + G_GUI_INNER_MARGIN; } mainButton->size(visible, mainButton->h()); /* Reposition everything else */ 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()) { p = k; break; } child(i)->position(child(p)->x() + child(p)->w() + G_GUI_INNER_MARGIN, child(i)->y()); } init_sizes(); // Resets the internal array of widget sizes and positions } /* -------------------------------------------------------------------------- */ bool geChannel::handleKey(int e) { 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 playButton->value(1); return true; } if (e == FL_KEYUP) { playButton->value(0); return true; } return false; } /* -------------------------------------------------------------------------- */ const c::channel::Data& geChannel::getData() const { return m_channel; } } // namespace giada::v giada-0.22.0/src/gui/elems/mainWindow/keyboard/channel.h000066400000000000000000000064341425106661500230320ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_CHANNEL_H #define GE_CHANNEL_H #include "core/types.h" #include "glue/channel.h" #include namespace giada::v { class geDial; class geButton; class geChannelStatus; class geStatusButton; class geChannelButton; class geMidiActivity; class geChannel : public Fl_Group { public: geChannel(int x, int y, int w, int h, c::channel::Data d); void draw() override; /* refresh Updates graphics. */ virtual void refresh(); /* getColumnId Returns the ID of the column this channel resides in. */ ID getColumnId() const; /* handleKey Performs some UI-related operations when the bound key is pressed. Returns whether the bound key has been pressed or not. */ bool handleKey(int e); /* getData Returns a reference to the internal data. Read-only. */ const c::channel::Data& getData() const; geStatusButton* playButton; geButton* arm; geChannelStatus* status; geChannelButton* mainButton; geMidiActivity* midiActivity; geStatusButton* mute; geStatusButton* solo; geDial* vol; #ifdef WITH_VST geStatusButton* fx; #endif protected: /* Define some breakpoints for dynamic resize. BREAK_DELTA: base amount of pixels to shrink sampleButton. */ #ifdef WITH_VST static const int BREAK_READ_ACTIONS = 240; static const int BREAK_MODE_BOX = 216; static const int BREAK_FX = 192; static const int BREAK_ARM = 168; #else static const int BREAK_READ_ACTIONS = 216; static const int BREAK_MODE_BOX = 192; static const int BREAK_ARM = 168; #endif static void cb_arm(Fl_Widget* /*w*/, void* p); static void cb_mute(Fl_Widget* /*w*/, void* p); static void cb_solo(Fl_Widget* /*w*/, void* p); static void cb_changeVol(Fl_Widget* /*w*/, void* p); #ifdef WITH_VST static void cb_openFxWindow(Fl_Widget* /*w*/, void* p); #endif void cb_mute(); void cb_arm(); void cb_solo(); void cb_changeVol(); #ifdef WITH_VST void cb_openFxWindow(); #endif /* blink Blinks button when channel is in wait/ending status. */ void blink(); /* packWidgets Spread widgets across available space. */ void packWidgets(); /* m_channel Channel's data. */ c::channel::Data m_channel; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/mainWindow/keyboard/channelButton.cpp000066400000000000000000000065601425106661500245610ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "channelButton.h" #include "core/channels/channel.h" #include "core/const.h" #include "core/model/model.h" #include "glue/channel.h" #include "src/core/actions/actions.h" #include "utils/string.h" #include namespace giada { namespace v { geChannelButton::geChannelButton(int x, int y, int w, int h, const c::channel::Data& d) : geButton(x, y, w, h) , m_channel(d) { } /* -------------------------------------------------------------------------- */ void geChannelButton::refresh() { 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(); if (m_channel.key == 0) return; /* draw background */ fl_rectf(x() + 1, y() + 1, 18, h() - 2, bgColor0); /* draw m_key */ fl_color(G_COLOR_LIGHT_2); fl_font(FL_HELVETICA, 11); fl_draw(std::string(1, static_cast(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; bdColor = G_COLOR_GREY_4; txtColor = G_COLOR_LIGHT_2; if (l) label(l); } /* -------------------------------------------------------------------------- */ void geChannelButton::setPlayMode() { bgColor0 = G_COLOR_LIGHT_1; bdColor = G_COLOR_LIGHT_1; txtColor = G_COLOR_GREY_1; } /* -------------------------------------------------------------------------- */ void geChannelButton::setEndingMode() { bgColor0 = G_COLOR_GREY_4; } } // namespace v } // namespace giada giada-0.22.0/src/gui/elems/mainWindow/keyboard/channelButton.h000066400000000000000000000033251425106661500242220ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_CHANNEL_BUTTON_H #define GE_CHANNEL_BUTTON_H #include "gui/elems/basics/button.h" 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 setInputRecordMode(); void setActionRecordMode(); protected: const c::channel::Data& m_channel; }; } // namespace v } // namespace giada #endif giada-0.22.0/src/gui/elems/mainWindow/keyboard/channelMode.cpp000066400000000000000000000100261425106661500241620ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ge_modeBox * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "channelMode.h" #include "core/channels/channel.h" #include "core/channels/samplePlayer.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 "utils/gui.h" #include #include namespace giada::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) { 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 . basic . pause", 0, cb_changeMode, (void*)SamplePlayerMode::SINGLE_BASIC_PAUSE); 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(m_channel.sample->mode)); } /* -------------------------------------------------------------------------- */ 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_BASIC_PAUSE: fl_draw_pixmap(oneshotBasicPause_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; default: assert(false); 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(mode)); } } // namespace giada::vgiada-0.22.0/src/gui/elems/mainWindow/keyboard/channelMode.h000066400000000000000000000031261425106661500236320ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ge_modeBox * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_CHANNEL_MODE_H #define GE_CHANNEL_MODE_H #include 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); c::channel::Data& m_channel; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/mainWindow/keyboard/channelStatus.cpp000066400000000000000000000051531425106661500245660ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/mainWindow/keyboard/channelStatus.h" #include "core/const.h" #include "glue/channel.h" #include "utils/math.h" #include namespace giada::v { geChannelStatus::geChannelStatus(int x, int y, int w, int h, c::channel::Data& d) : 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 const ChannelStatus playStatus = m_channel.getPlayStatus(); const ChannelStatus recStatus = m_channel.getRecStatus(); const Frame tracker = m_channel.sample->getTracker(); const Frame begin = m_channel.sample->getBegin(); const Frame end = m_channel.sample->getEnd(); const Pixel pos = u::math::map(tracker, begin, end, 0, w()); 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) { fl_rect(x(), y(), w(), h(), G_COLOR_LIGHT_1); fl_rectf(x() + 1, y() + 1, pos, h() - 2, 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, pos, h() - 2, G_COLOR_GREY_4); } } } // namespace giada::vgiada-0.22.0/src/gui/elems/mainWindow/keyboard/channelStatus.h000066400000000000000000000027561425106661500242410ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ge_status * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_CHANNEL_STATUS_H #define GE_CHANNEL_STATUS_H #include namespace giada::c::channel { struct Data; } 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; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/mainWindow/keyboard/column.cpp000066400000000000000000000147401425106661500232510ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/mainWindow/keyboard/column.h" #include "core/model/model.h" #include "glue/channel.h" #include "gui/dialogs/warnings.h" #include "gui/elems/basics/boxtypes.h" #include "gui/elems/basics/resizerBar.h" #include "gui/elems/mainWindow/keyboard/keyboard.h" #include "gui/elems/mainWindow/keyboard/midiChannel.h" #include "gui/elems/mainWindow/keyboard/sampleChannel.h" #include "gui/ui.h" #include "utils/fs.h" #include "utils/gui.h" #include "utils/log.h" #include "utils/string.h" #include #include #include extern giada::v::Ui g_ui; namespace giada::v { namespace { enum class Menu { ADD_SAMPLE_CHANNEL = 0, ADD_MIDI_CHANNEL, REMOVE }; void MenuCallback(Fl_Widget* w, void* v) { const geColumn* column = static_cast(w); switch ((Menu)(intptr_t)v) { case Menu::ADD_SAMPLE_CHANNEL: c::channel::addChannel(column->id, ChannelType::SAMPLE); break; case Menu::ADD_MIDI_CHANNEL: c::channel::addChannel(column->id, ChannelType::MIDI); break; case Menu::REMOVE: static_cast(column->parent())->deleteColumn(column->id); break; } } } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ geColumn::geColumn(int X, int Y, int W, int H, ID id, geResizerBar* 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; Fl_Widget* last = m_channels.size() == 0 ? static_cast(m_addChannelBtn) : m_channels.back(); 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); geResizerBar* bar = new geResizerBar(x(), gch->y() + gch->h(), w(), G_GUI_INNER_MARGIN, G_GUI_UNIT, geResizerBar::Direction::VERTICAL, geResizerBar::Mode::MOVE); /* Update the column height while dragging the resizer bar. */ 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) { resizable(this); c::channel::setHeight(channelId, w.h()); }; m_channels.push_back(gch); /* 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. */ resizable(nullptr); add(gch); add(bar); size(w(), computeHeight()); init_sizes(); resizable(this); return gch; } /* -------------------------------------------------------------------------- */ void geColumn::cb_addChannel() { G_DEBUG("Column id: " << id); Fl_Menu_Item menu[] = { u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_COLUMN_BUTTON_ADDSAMPLECHANNEL), MenuCallback, (void*)Menu::ADD_SAMPLE_CHANNEL), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_COLUMN_BUTTON_ADDMIDICHANNEL), MenuCallback, (void*)Menu::ADD_MIDI_CHANNEL), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_COLUMN_BUTTON_REMOVE), MenuCallback, (void*)Menu::REMOVE), {}}; if (countChannels() > 0) menu[(int)Menu::REMOVE].deactivate(); Fl_Menu_Button b(0, 0, 100, 50); b.box(G_CUSTOM_BORDER_BOX); b.textsize(G_GUI_FONT_SIZE_BASE); b.textcolor(G_COLOR_LIGHT_2); b.color(G_COLOR_GREY_2); const Fl_Menu_Item* m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, &b); if (m != nullptr) m->do_callback(this, m->user_data()); } /* -------------------------------------------------------------------------- */ geChannel* geColumn::getChannel(ID channelId) const { for (geChannel* c : m_channels) if (c->getData().id == channelId) return c; return nullptr; } /* -------------------------------------------------------------------------- */ void geColumn::init() { Fl_Group::clear(); m_channels.clear(); m_addChannelBtn = new geButton(x(), y(), w(), G_GUI_UNIT, g_ui.langMapper.get(LangMap::MAIN_COLUMN_BUTTON)); m_addChannelBtn->callback(cb_addChannel, (void*)this); add(m_addChannelBtn); } /* -------------------------------------------------------------------------- */ void geColumn::forEachChannel(std::function f) const { for (geChannel* c : m_channels) f(*c); } /* -------------------------------------------------------------------------- */ int geColumn::countChannels() const { return m_channels.size(); } /* -------------------------------------------------------------------------- */ int geColumn::computeHeight() const { int out = 0; for (const geChannel* c : m_channels) out += c->h() + G_GUI_INNER_MARGIN; return out + m_addChannelBtn->h() + G_GUI_INNER_MARGIN; } } // namespace giada::vgiada-0.22.0/src/gui/elems/mainWindow/keyboard/column.h000066400000000000000000000041071425106661500227120ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_COLUMN_H #define GE_COLUMN_H #include "core/types.h" #include "glue/channel.h" #include #include #include namespace giada::v { class geButton; class geResizerBar; 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. */ geChannel* addChannel(c::channel::Data d); /* refreshChannels Updates channels' graphical statues. Called on each GUI cycle. */ void refresh(); void init(); void forEachChannel(std::function f) const; ID id; geResizerBar* resizerBar; private: static void cb_addChannel(Fl_Widget* /*w*/, void* p); void cb_addChannel(); int countChannels() const; int computeHeight() const; std::vector m_channels; geButton* m_addChannelBtn; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/mainWindow/keyboard/keyboard.cpp000066400000000000000000000207331425106661500235530ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/mainWindow/keyboard/keyboard.h" #include "glue/channel.h" #include "glue/io.h" #include "gui/dialogs/warnings.h" #include "gui/dispatcher.h" #include "gui/elems/basics/boxtypes.h" #include "gui/elems/basics/dial.h" #include "gui/elems/basics/resizerBar.h" #include "gui/elems/mainWindow/keyboard/channelButton.h" #include "gui/elems/mainWindow/keyboard/column.h" #include "gui/elems/mainWindow/keyboard/midiActivity.h" #include "gui/elems/mainWindow/keyboard/sampleChannel.h" #include "gui/ui.h" #include "utils/fs.h" #include "utils/log.h" #include "utils/string.h" #include "utils/vector.h" #include #include extern giada::v::Ui g_ui; namespace giada::v { geKeyboard::geKeyboard() : geScroll(Fl_Scroll::BOTH_ALWAYS) , m_addColumnBtn(nullptr) { end(); init(); rebuild(); } /* -------------------------------------------------------------------------- */ ID geKeyboard::getChannelColumnId(ID channelId) const { return getChannel(channelId)->getColumnId(); } /* -------------------------------------------------------------------------- */ void geKeyboard::init() { m_columnId = m::IdManager(); deleteAllColumns(); /* Add 6 empty columns as initial layout. */ layout.clear(); layout.push_back({1, G_DEFAULT_COLUMN_WIDTH}); layout.push_back({2, G_DEFAULT_COLUMN_WIDTH}); layout.push_back({3, G_DEFAULT_COLUMN_WIDTH}); layout.push_back({4, G_DEFAULT_COLUMN_WIDTH}); layout.push_back({5, G_DEFAULT_COLUMN_WIDTH}); 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); add(m_addColumnBtn); } /* -------------------------------------------------------------------------- */ void geKeyboard::setChannelVolume(ID channelId, float v) { getChannel(channelId)->vol->value(v); } /* -------------------------------------------------------------------------- */ void geKeyboard::notifyMidiIn(ID channelId) { getChannel(channelId)->midiActivity->in->lit(); } void geKeyboard::notifyMidiOut(ID channelId) { getChannel(channelId)->midiActivity->out->lit(); } /* -------------------------------------------------------------------------- */ 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 g_ui.dispatcher.dispatchKey(e); return 1; } case FL_DND_ENTER: // return(1) for these events to 'accept' dnd case FL_DND_DRAG: case FL_DND_RELEASE: { return 1; } case FL_PASTE: { // handle actual drop (paste) operation const geColumn* c = getColumnAtCursor(Fl::event_x()); if (c != nullptr) c::channel::addAndLoadChannels(c->id, getDroppedFilePaths()); return 1; } } return Fl_Group::handle(e); // Assume the buttons won't handle the Keyboard events } /* -------------------------------------------------------------------------- */ void geKeyboard::draw() { Fl_Scroll::draw(); /* Paint columns background. Use a clip to draw only what's visible. */ fl_color(G_COLOR_GREY_1_5); fl_push_clip( x(), y(), w() - scrollbar_size() - (G_GUI_OUTER_MARGIN * 2), h() - scrollbar_size() - (G_GUI_OUTER_MARGIN * 2)); 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() /* If this is not the first column... */ if (m_columns.size() > 0) colx = m_columns.back()->x() + m_columns.back()->w() + COLUMN_GAP; /* Generate new index. If not passed in. */ m_columnId.set(id); /* Add a new column + a new resizer bar. */ geResizerBar* bar = new geResizerBar(colx + width, y(), COLUMN_GAP, h(), G_MIN_COLUMN_WIDTH, geResizerBar::Direction::HORIZONTAL); 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*/) { storeLayout(); }; add(column); add(bar); m_columns.push_back(column); /* And then shift the "add column" button on the rightmost edge. */ m_addColumnBtn->position(colx + width + COLUMN_GAP, y()); redraw(); } /* -------------------------------------------------------------------------- */ void geKeyboard::forEachChannel(std::function f) const { for (geColumn* column : m_columns) column->forEachChannel(f); } void geKeyboard::forEachColumn(std::function f) const { for (geColumn* column : m_columns) f(*column); } /* -------------------------------------------------------------------------- */ geColumn* geKeyboard::getColumn(ID id) { for (geColumn* c : m_columns) if (c->id == id) return c; assert(false); return nullptr; } geColumn* geKeyboard::getColumnAtCursor(Pixel px) { px += xposition(); for (geColumn* c : m_columns) if (px > c->x() && px <= c->x() + c->w()) return c; return nullptr; } /* -------------------------------------------------------------------------- */ const geChannel* geKeyboard::getChannel(ID channelId) const { for (geColumn* column : m_columns) { geChannel* c = column->getChannel(channelId); if (c != nullptr) return c; } assert(false); return nullptr; } geChannel* geKeyboard::getChannel(ID channelId) { return const_cast(const_cast(this)->getChannel(channelId)); } /* -------------------------------------------------------------------------- */ std::vector geKeyboard::getDroppedFilePaths() const { std::vector paths = u::string::split(Fl::event_text(), "\n"); 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()}); } } // namespace giada::vgiada-0.22.0/src/gui/elems/mainWindow/keyboard/keyboard.h000066400000000000000000000066101425106661500232160ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_KEYBOARD_H #define GE_KEYBOARD_H #include "core/const.h" #include "core/idManager.h" #include "gui/elems/basics/scroll.h" #include #include namespace giada::v { class geButton; class geResizerBar; class geColumn; class geChannel; class geKeyboard : public geScroll { public: struct ColumnLayout { ID id; int width; }; geKeyboard(); int handle(int e) override; void draw() override; /* getChannelColumnId Given a channel ID, returns the ID of the column it belongs to. */ ID getChannelColumnId(ID channelId) const; /* rebuild Rebuilds this widget from scratch. Used when the model has changed. */ void rebuild(); /* refresh Refreshes each column's channel, called on each GUI cycle. */ void refresh(); /* deleteColumn Deletes column by id. */ void deleteColumn(ID id); /* deleteAllColumns Deletes all columns from the stack. */ void deleteAllColumns(); void setChannelVolume(ID channelId, float v); void notifyMidiIn(ID channelId); void notifyMidiOut(ID channelId); /* init Builds the default setup of empty columns. */ void init(); void forEachChannel(std::function f) const; void forEachColumn(std::function f) const; /* layout The column layout. Each element is a column with a specific width. */ std::vector layout; private: static constexpr int COLUMN_GAP = 20; static void cb_addColumn(Fl_Widget* /*w*/, void* p); void cb_addColumn(); 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 event. */ std::vector getDroppedFilePaths() const; /* getColumn Returns the column given the ID. */ geColumn* getColumn(ID id); /* getColumnAtCursor Returns the column below the cursor. */ geColumn* getColumnAtCursor(Pixel x); /* getChannel Given a channel ID returns the UI channel it belongs to. */ geChannel* getChannel(ID channelId); const geChannel* getChannel(ID channelId) const; /* storeLayout Stores the current column layout into the layout vector. */ void storeLayout(); m::IdManager m_columnId; std::vector m_columns; geButton* m_addColumnBtn; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/mainWindow/keyboard/midiActivity.cpp000066400000000000000000000052111425106661500244040ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/mainWindow/keyboard/midiActivity.h" #include "core/const.h" #include "gui/elems/basics/flex.h" #include "gui/ui.h" #include extern giada::v::Ui g_ui; namespace giada::v { geMidiActivity::geLed::geLed() : Fl_Button(0, 0, 0, 0) , m_decay(0) // decay > 0: led is on { } /* -------------------------------------------------------------------------- */ void geMidiActivity::geLed::draw() { int bgColor = G_COLOR_GREY_2; int bdColor = G_COLOR_GREY_4; if (m_decay > 0) // If led is on { m_decay = (m_decay + 1) % (G_GUI_FPS / 4); bgColor = G_COLOR_LIGHT_2; bdColor = G_COLOR_LIGHT_2; } fl_rectf(x(), y(), w(), h(), bgColor); // background fl_rect(x(), y(), w(), h(), bdColor); // border } /* -------------------------------------------------------------------------- */ void geMidiActivity::geLed::lit() { m_decay = 1; } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ geMidiActivity::geMidiActivity(int x, int y, int w, int h) : Fl_Group(x, y, w, h) { end(); geFlex* container = new geFlex(x, y, w, h, Direction::VERTICAL, G_GUI_INNER_MARGIN); { out = new geLed(); in = new geLed(); container->add(out); container->add(in); container->end(); } add(container); resizable(container); copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_LABEL_MIDIACTIVITY)); } } // namespace giada::vgiada-0.22.0/src/gui/elems/mainWindow/keyboard/midiActivity.h000066400000000000000000000030151425106661500240510ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_MIDI_ACTIVITY_H #define GE_MIDI_ACTIVITY_H #include #include namespace giada::v { class geMidiActivity : public Fl_Group { public: class geLed : public Fl_Button { public: geLed(); void draw() override; void lit(); private: int m_decay; }; geMidiActivity(int x, int y, int w, int h); geLed* out; geLed* in; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/mainWindow/keyboard/midiChannel.cpp000066400000000000000000000202601425106661500241610ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) G_GUI_UNIT10-G_GUI_UNIT17 Giovanni A. Zuliani | Monocasual * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/mainWindow/keyboard/midiChannel.h" #include "core/const.h" #include "core/graphics.h" #include "glue/channel.h" #include "glue/io.h" #include "glue/layout.h" #include "glue/recorder.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/dial.h" #include "gui/elems/basics/statusButton.h" #include "gui/elems/mainWindow/keyboard/column.h" #include "gui/elems/mainWindow/keyboard/midiActivity.h" #include "gui/elems/mainWindow/keyboard/midiChannelButton.h" #include "gui/ui.h" #include "utils/gui.h" #include "utils/string.h" #include #include extern giada::v::Ui g_ui; namespace giada::v { namespace { enum class Menu { EDIT_ACTIONS = 0, CLEAR_ACTIONS, CLEAR_ACTIONS_ALL, __END_CLEAR_ACTION_SUBMENU__, SETUP_KEYBOARD_INPUT, SETUP_MIDI_INPUT, SETUP_MIDI_OUTPUT, RENAME_CHANNEL, CLONE_CHANNEL, DELETE_CHANNEL }; /* -------------------------------------------------------------------------- */ void menuCallback(Fl_Widget* w, void* v) { const geMidiChannel* gch = static_cast(w); const c::channel::Data& data = gch->getData(); switch ((Menu)(intptr_t)v) { case Menu::CLEAR_ACTIONS: case Menu::__END_CLEAR_ACTION_SUBMENU__: break; case Menu::EDIT_ACTIONS: c::layout::openMidiActionEditor(data.id); break; case Menu::CLEAR_ACTIONS_ALL: c::recorder::clearAllActions(data.id); break; case Menu::SETUP_KEYBOARD_INPUT: c::layout::openKeyGrabberWindow(data.key, [channelId = data.id](int key) { return c::io::channel_setKey(channelId, key); }); break; case Menu::SETUP_MIDI_INPUT: c::layout::openChannelMidiInputWindow(data.id); break; case Menu::SETUP_MIDI_OUTPUT: c::layout::openMidiChannelMidiOutputWindow(data.id); break; case Menu::CLONE_CHANNEL: c::channel::cloneChannel(data.id); break; case Menu::RENAME_CHANNEL: c::layout::openRenameChannelWindow(data); break; case Menu::DELETE_CHANNEL: c::channel::deleteChannel(data.id); break; } } } // namespace /* -------------------------------------------------------------------------- */ geMidiChannel::geMidiChannel(int X, int Y, int W, int H, c::channel::Data d) : geChannel(X, Y, W, H, d) , m_data(d) { 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(), G_GUI_UNIT, H, m_channel); midiActivity = new geMidiActivity(mainButton->x() + mainButton->w() + G_GUI_INNER_MARGIN, y(), 10, h()); mute = new geStatusButton(midiActivity->x() + midiActivity->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); #else 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(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_LABEL_PLAY)); arm->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_LABEL_ARM)); mute->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_LABEL_MUTE)); solo->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_LABEL_SOLO)); #if defined(WITH_VST) fx->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_LABEL_FX)); #endif vol->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_LABEL_VOLUME)); #ifdef WITH_VST fx->setStatus(m_channel.plugins.size() > 0); #endif playButton->callback(cb_playButton, (void*)this); playButton->when(FL_WHEN_CHANGED); // On keypress && on keyrelease arm->type(FL_TOGGLE_BUTTON); arm->value(m_channel.isArmed()); arm->callback(cb_arm, (void*)this); #ifdef WITH_VST fx->callback(cb_openFxWindow, (void*)this); #endif mute->type(FL_TOGGLE_BUTTON); mute->callback(cb_mute, (void*)this); solo->type(FL_TOGGLE_BUTTON); solo->callback(cb_solo, (void*)this); mainButton->callback(cb_openMenu, (void*)this); vol->value(m_channel.volume); vol->callback(cb_changeVol, (void*)this); 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() { m_channel.viewDispatcher.dispatchTouch(*this, playButton->value()); } /* -------------------------------------------------------------------------- */ void geMidiChannel::cb_openMenu() { Fl_Menu_Item rclick_menu[] = { u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_EDITACTIONS), menuCallback, (void*)Menu::EDIT_ACTIONS), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_CLEARACTIONS), menuCallback, (void*)Menu::CLEAR_ACTIONS, FL_SUBMENU), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_CLEARACTIONS_ALL), menuCallback, (void*)Menu::CLEAR_ACTIONS_ALL), {}, u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_KEYBOARDINPUT), menuCallback, (void*)Menu::SETUP_KEYBOARD_INPUT), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_MIDIINPUT), menuCallback, (void*)Menu::SETUP_MIDI_INPUT), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_MIDIOUTPUT), menuCallback, (void*)Menu::SETUP_MIDI_OUTPUT), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_RENAME), menuCallback, (void*)Menu::RENAME_CHANNEL), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_CLONE), menuCallback, (void*)Menu::CLONE_CHANNEL), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_DELETE), menuCallback, (void*)Menu::DELETE_CHANNEL), {}}; /* No 'clear actions' if there are no actions. */ if (!m_data.hasActions) rclick_menu[(int)Menu::CLEAR_ACTIONS].deactivate(); Fl_Menu_Button b(0, 0, 100, 50); b.box(G_CUSTOM_BORDER_BOX); b.textsize(G_GUI_FONT_SIZE_BASE); b.textcolor(G_COLOR_LIGHT_2); b.color(G_COLOR_GREY_2); const Fl_Menu_Item* m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, &b); if (m != nullptr) m->do_callback(this, m->user_data()); return; } /* -------------------------------------------------------------------------- */ void geMidiChannel::resize(int X, int Y, int W, int H) { geChannel::resize(X, Y, W, H); arm->hide(); #ifdef WITH_VST fx->hide(); #endif if (w() > BREAK_ARM) arm->show(); #ifdef WITH_VST if (w() > BREAK_FX) fx->show(); #endif packWidgets(); } } // namespace giada::vgiada-0.22.0/src/gui/elems/mainWindow/keyboard/midiChannel.h000066400000000000000000000032361425106661500236320ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_MIDI_CHANNEL_H #define GE_MIDI_CHANNEL_H #include "channel.h" #include "channelButton.h" namespace giada { namespace v { class geMidiChannel : public geChannel { public: 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: static void cb_playButton(Fl_Widget* /*w*/, void* p); static void cb_openMenu(Fl_Widget* /*w*/, void* p); void cb_playButton(); void cb_openMenu(); c::channel::Data m_data; }; } // namespace v } // namespace giada #endif giada-0.22.0/src/gui/elems/mainWindow/keyboard/midiChannelButton.cpp000066400000000000000000000037661425106661500253710ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "midiChannelButton.h" #include "glue/channel.h" #include "utils/string.h" namespace giada { namespace v { geMidiChannelButton::geMidiChannelButton(int x, int y, int w, int h, const c::channel::Data& d) : geChannelButton(x, y, w, h, d) { } /* -------------------------------------------------------------------------- */ void geMidiChannelButton::refresh() { geChannelButton::refresh(); refreshLabel(); if (m_channel.isRecordingAction() && m_channel.isArmed()) setActionRecordMode(); redraw(); } /* -------------------------------------------------------------------------- */ void geMidiChannelButton::refreshLabel() { std::string l = m_channel.name.empty() ? "-- MIDI --" : m_channel.name; if (m_channel.midi->isOutputEnabled()) l += " (ch " + std::to_string(m_channel.midi->getFilter() + 1) + " out)"; copy_label(l.c_str()); } } // namespace v } // namespace giada giada-0.22.0/src/gui/elems/mainWindow/keyboard/midiChannelButton.h000066400000000000000000000027541425106661500250320ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_MIDI_CHANNEL_BUTTON_H #define GE_MIDI_CHANNEL_BUTTON_H #include "channelButton.h" 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 refreshLabel(); }; } // namespace v } // namespace giada #endif giada-0.22.0/src/gui/elems/mainWindow/keyboard/sampleChannel.cpp000066400000000000000000000323441425106661500245260ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/mainWindow/keyboard/sampleChannel.h" #include "core/graphics.h" #include "glue/channel.h" #include "glue/events.h" #include "glue/io.h" #include "glue/layout.h" #include "glue/recorder.h" #include "glue/storage.h" #include "gui/dispatcher.h" #include "gui/elems/basics/boxtypes.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/dial.h" #include "gui/elems/basics/statusButton.h" #include "gui/elems/mainWindow/keyboard/channelMode.h" #include "gui/elems/mainWindow/keyboard/channelStatus.h" #include "gui/elems/mainWindow/keyboard/column.h" #include "gui/elems/mainWindow/keyboard/keyboard.h" #include "gui/elems/mainWindow/keyboard/midiActivity.h" #include "gui/elems/mainWindow/keyboard/sampleChannelButton.h" #include "gui/ui.h" #include "utils/gui.h" extern giada::v::Ui g_ui; namespace giada::v { namespace { enum class Menu { INPUT_MONITOR = 0, OVERDUB_PROTECTION, LOAD_SAMPLE, EXPORT_SAMPLE, SETUP_KEYBOARD_INPUT, SETUP_MIDI_INPUT, SETUP_MIDI_OUTPUT, EDIT_SAMPLE, EDIT_ACTIONS, CLEAR_ACTIONS, CLEAR_ACTIONS_ALL, CLEAR_ACTIONS_VOLUME, CLEAR_ACTIONS_START_STOP, __END_CLEAR_ACTIONS_SUBMENU__, RENAME_CHANNEL, CLONE_CHANNEL, FREE_CHANNEL, DELETE_CHANNEL }; /* -------------------------------------------------------------------------- */ void menuCallback(Fl_Widget* w, void* v) { const geSampleChannel* gch = static_cast(w); const c::channel::Data& data = gch->getData(); 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: { c::layout::openBrowserForSampleLoad(data.id); break; } case Menu::EXPORT_SAMPLE: { c::layout::openBrowserForSampleSave(data.id); break; } case Menu::SETUP_KEYBOARD_INPUT: { c::layout::openKeyGrabberWindow(data.key, [channelId = data.id](int key) { return c::io::channel_setKey(channelId, key); }); break; } case Menu::SETUP_MIDI_INPUT: { c::layout::openChannelMidiInputWindow(data.id); break; } case Menu::SETUP_MIDI_OUTPUT: { c::layout::openSampleChannelMidiOutputWindow(data.id); break; } case Menu::EDIT_SAMPLE: { c::layout::openSampleEditor(data.id); break; } case Menu::EDIT_ACTIONS: { c::layout::openSampleActionEditor(data.id); 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: { c::layout::openRenameChannelWindow(data); break; } case Menu::FREE_CHANNEL: { c::channel::freeChannel(data.id); break; } case Menu::DELETE_CHANNEL: { c::channel::deleteChannel(data.id); break; } } } } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ geSampleChannel::geSampleChannel(int X, int Y, int W, int H, c::channel::Data d) : geChannel(X, Y, W, H, d) { 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(), G_GUI_UNIT, H, m_channel); midiActivity = new geMidiActivity(mainButton->x() + mainButton->w() + G_GUI_INNER_MARGIN, y(), 10, h()); readActions = new geStatusButton(midiActivity->x() + midiActivity->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); #else 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(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_LABEL_PLAY)); arm->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_LABEL_ARM)); status->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_LABEL_STATUS)); readActions->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_LABEL_READACTIONS)); modeBox->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_LABEL_MODEBOX)); mute->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_LABEL_MUTE)); solo->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_LABEL_SOLO)); #if defined(WITH_VST) fx->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_LABEL_FX)); #endif vol->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_LABEL_VOLUME)); #ifdef WITH_VST fx->setStatus(m_channel.plugins.size() > 0); #endif playButton->callback(cb_playButton, (void*)this); playButton->when(FL_WHEN_CHANGED); // On keypress && on keyrelease arm->type(FL_TOGGLE_BUTTON); arm->value(m_channel.isArmed()); arm->callback(cb_arm, (void*)this); #ifdef WITH_VST fx->callback(cb_openFxWindow, (void*)this); #endif mute->type(FL_TOGGLE_BUTTON); mute->callback(cb_mute, (void*)this); solo->type(FL_TOGGLE_BUTTON); solo->callback(cb_solo, (void*)this); mainButton->callback(cb_openMenu, (void*)this); readActions->callback(cb_readActions, (void*)this); vol->value(m_channel.volume); vol->callback(cb_changeVol, (void*)this); 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_readActions(Fl_Widget* /*w*/, void* p) { ((geSampleChannel*)p)->cb_readActions(); } /* -------------------------------------------------------------------------- */ void geSampleChannel::cb_playButton() { m_channel.viewDispatcher.dispatchTouch(*this, playButton->value()); } /* -------------------------------------------------------------------------- */ void geSampleChannel::cb_openMenu() { /* If you're recording (input or actions) no menu is allowed; you can't do anything, especially deallocate the channel. */ if (m_channel.isRecordingAction() || m_channel.isRecordingInput()) return; Fl_Menu_Item rclick_menu[] = { u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_INPUTMONITOR), menuCallback, (void*)Menu::INPUT_MONITOR, FL_MENU_TOGGLE | (m_channel.sample->getInputMonitor() ? FL_MENU_VALUE : 0)), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_OVERDUBPROTECTION), menuCallback, (void*)Menu::OVERDUB_PROTECTION, FL_MENU_TOGGLE | FL_MENU_DIVIDER | (m_channel.sample->getOverdubProtection() ? FL_MENU_VALUE : 0)), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_LOADSAMPLE), menuCallback, (void*)Menu::LOAD_SAMPLE), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_EXPORTSAMPLE), menuCallback, (void*)Menu::EXPORT_SAMPLE), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_KEYBOARDINPUT), menuCallback, (void*)Menu::SETUP_KEYBOARD_INPUT), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_MIDIINPUT), menuCallback, (void*)Menu::SETUP_MIDI_INPUT), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_MIDIOUTPUT), menuCallback, (void*)Menu::SETUP_MIDI_OUTPUT), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_EDITSAMPLE), menuCallback, (void*)Menu::EDIT_SAMPLE), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_EDITACTIONS), menuCallback, (void*)Menu::EDIT_ACTIONS), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_CLEARACTIONS), menuCallback, (void*)Menu::CLEAR_ACTIONS, FL_SUBMENU), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_CLEARACTIONS_ALL), menuCallback, (void*)Menu::CLEAR_ACTIONS_ALL), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_CLEARACTIONS_VOLUME), menuCallback, (void*)Menu::CLEAR_ACTIONS_VOLUME), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_CLEARACTIONS_STARTSTOP), menuCallback, (void*)Menu::CLEAR_ACTIONS_START_STOP), {}, u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_RENAME), menuCallback, (void*)Menu::RENAME_CHANNEL), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_CLONE), menuCallback, (void*)Menu::CLONE_CHANNEL), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_FREE), menuCallback, (void*)Menu::FREE_CHANNEL), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_MENU_DELETE), menuCallback, (void*)Menu::DELETE_CHANNEL), {}}; 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(); /* 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(); Fl_Menu_Button b(0, 0, 100, 50); b.box(G_CUSTOM_BORDER_BOX); b.textsize(G_GUI_FONT_SIZE_BASE); b.textcolor(G_COLOR_LIGHT_2); b.color(G_COLOR_GREY_2); const Fl_Menu_Item* m = rclick_menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, &b); if (m != nullptr) m->do_callback(this, m->user_data()); return; } /* -------------------------------------------------------------------------- */ void geSampleChannel::cb_readActions() { if (Fl::event_shift()) c::events::killReadActionsChannel(m_channel.id, Thread::MAIN); else c::events::toggleReadActionsChannel(m_channel.id, Thread::MAIN); } /* -------------------------------------------------------------------------- */ void geSampleChannel::refresh() { geChannel::refresh(); if (m_channel.sample->waveId != 0) { status->redraw(); if (m_channel.sample->getOverdubProtection()) arm->deactivate(); else arm->activate(); } if (m_channel.hasActions) { readActions->activate(); readActions->setStatus(m_channel.getReadActions()); } else readActions->deactivate(); } /* -------------------------------------------------------------------------- */ void geSampleChannel::draw() { const int ny = y() + (h() / 2) - (G_GUI_UNIT / 2); modeBox->resize(modeBox->x(), ny, G_GUI_UNIT, G_GUI_UNIT); readActions->resize(readActions->x(), ny, G_GUI_UNIT, G_GUI_UNIT); geChannel::draw(); } /* -------------------------------------------------------------------------- */ void geSampleChannel::resize(int X, int Y, int W, int H) { geChannel::resize(X, Y, W, H); arm->hide(); modeBox->hide(); readActions->hide(); #ifdef WITH_VST fx->hide(); #endif if (w() > BREAK_ARM) arm->show(); #ifdef WITH_VST if (w() > BREAK_FX) fx->show(); #endif if (w() > BREAK_MODE_BOX) modeBox->show(); if (w() > BREAK_READ_ACTIONS) readActions->show(); packWidgets(); } } // namespace giada::vgiada-0.22.0/src/gui/elems/mainWindow/keyboard/sampleChannel.h000066400000000000000000000035341425106661500241720ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_SAMPLE_CHANNEL_H #define GE_SAMPLE_CHANNEL_H #include "channel.h" #include "glue/channel.h" 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: 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(); }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp000066400000000000000000000052031425106661500257140ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "sampleChannelButton.h" #include "glue/channel.h" #include "gui/dialogs/mainWindow.h" #include "gui/ui.h" #include "keyboard.h" #include "sampleChannel.h" #include "utils/fs.h" #include "utils/string.h" #include extern giada::v::Ui g_ui; namespace giada::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.getPlayStatus()) { case ChannelStatus::MISSING: case ChannelStatus::WRONG: label(g_ui.langMapper.get(LangMap::MAIN_CHANNEL_SAMPLENOTFOUND)); break; default: label(m_channel.sample->waveId == 0 ? g_ui.langMapper.get(LangMap::MAIN_CHANNEL_NOSAMPLE) : m_channel.name.c_str()); break; } } /* -------------------------------------------------------------------------- */ void geSampleChannelButton::refresh() { geChannelButton::refresh(); if (m_channel.isRecordingInput() && m_channel.isArmed()) setInputRecordMode(); 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; } } return ret; } } // namespace giada::v giada-0.22.0/src/gui/elems/mainWindow/keyboard/sampleChannelButton.h000066400000000000000000000027601425106661500253660ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_SAMPLE_CHANNEL_BUTTON_H #define GE_SAMPLE_CHANNEL_BUTTON_H #include "channelButton.h" 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; }; } // namespace v } // namespace giada #endif giada-0.22.0/src/gui/elems/mainWindow/mainIO.cpp000066400000000000000000000106631425106661500213300ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ------------------------------------------------------------------------------ * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * --------------------------------------------------------------------------- */ #include "gui/elems/mainWindow/mainIO.h" #include "core/const.h" #include "core/graphics.h" #include "glue/channel.h" #include "glue/events.h" #include "glue/layout.h" #include "glue/main.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/dial.h" #include "gui/elems/basics/statusButton.h" #include "gui/elems/soundMeter.h" #include "gui/ui.h" #include "utils/gui.h" #ifdef WITH_VST #include "gui/elems/basics/statusButton.h" #endif extern giada::v::Ui g_ui; namespace giada::v { geMainIO::geMainIO() : geFlex(Direction::HORIZONTAL, G_GUI_INNER_MARGIN) { m_outMeter = new geSoundMeter(0, 0, 0, 0); m_inMeter = new geSoundMeter(0, 0, 0, 0); m_outVol = new geDial(0, 0, 0, 0); m_inVol = new geDial(0, 0, 0, 0); m_inToOut = new geButton(); #ifdef WITH_VST m_masterFxOut = new geStatusButton(0, 0, 0, 0, fxOff_xpm, fxOn_xpm); m_masterFxIn = new geStatusButton(0, 0, 0, 0, fxOff_xpm, fxOn_xpm); #endif #ifdef WITH_VST add(m_masterFxIn, G_GUI_UNIT); #endif add(m_inVol, G_GUI_UNIT); add(m_inMeter); add(m_inToOut, 12); add(m_outMeter); add(m_outVol, G_GUI_UNIT); #ifdef WITH_VST add(m_masterFxOut, G_GUI_UNIT); #endif end(); m_outMeter->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_IO_LABEL_OUTMETER)); m_inMeter->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_IO_LABEL_INMETER)); m_outVol->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_IO_LABEL_OUTVOL)); m_inVol->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_IO_LABEL_INVOL)); m_inToOut->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_IO_LABEL_INTOOUT)); #ifdef WITH_VST m_masterFxOut->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_IO_LABEL_FXOUT)); m_masterFxIn->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_IO_LABEL_FXIN)); #endif m_outVol->onChange = [](float v) { c::events::setMasterOutVolume(v, Thread::MAIN); }; m_inVol->onChange = [](float v) { c::events::setMasterInVolume(v, Thread::MAIN); }; m_inToOut->type(FL_TOGGLE_BUTTON); m_inToOut->onClick = [&inToOut = m_inToOut]() { c::main::setInToOut(inToOut->value()); }; #ifdef WITH_VST m_masterFxOut->onClick = [] { c::layout::openMasterOutPluginListWindow(); }; m_masterFxIn->onClick = [] { c::layout::openMasterInPluginListWindow(); }; #endif } /* -------------------------------------------------------------------------- */ void geMainIO::setOutVol(float v) { m_outVol->value(v); } void geMainIO::setInVol(float v) { m_inVol->value(v); } /* -------------------------------------------------------------------------- */ #ifdef WITH_VST void geMainIO::setMasterFxOutFull(bool v) { m_masterFxOut->setStatus(v); } void geMainIO::setMasterFxInFull(bool v) { m_masterFxIn->setStatus(v); } #endif /* -------------------------------------------------------------------------- */ void geMainIO::refresh() { m_outMeter->peak = m_io.getMasterOutPeak(); m_outMeter->ready = m_io.isKernelReady(); m_inMeter->peak = m_io.getMasterInPeak(); m_inMeter->ready = m_io.isKernelReady(); m_outMeter->redraw(); m_inMeter->redraw(); } /* -------------------------------------------------------------------------- */ void geMainIO::rebuild() { m_io = c::main::getIO(); m_outVol->value(m_io.masterOutVol); m_inVol->value(m_io.masterInVol); #ifdef WITH_VST m_masterFxOut->setStatus(m_io.masterOutHasPlugins); m_masterFxIn->setStatus(m_io.masterInHasPlugins); m_inToOut->value(m_io.inToOut); #endif } } // namespace giada::vgiada-0.22.0/src/gui/elems/mainWindow/mainIO.h000066400000000000000000000034661425106661500210000ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_MAIN_IO_H #define GE_MAIN_IO_H #include "glue/main.h" #include "gui/elems/basics/flex.h" namespace giada::v { class geDial; class geSoundMeter; class geButton; class geStatusButton; class geMainIO : public geFlex { public: geMainIO(); void refresh(); void rebuild(); void setOutVol(float v); void setInVol(float v); #ifdef WITH_VST void setMasterFxOutFull(bool v); void setMasterFxInFull(bool v); #endif private: c::main::IO m_io; geSoundMeter* m_outMeter; geSoundMeter* m_inMeter; geDial* m_outVol; geDial* m_inVol; geButton* m_inToOut; #ifdef WITH_VST geStatusButton* m_masterFxOut; geStatusButton* m_masterFxIn; #endif }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/mainWindow/mainMenu.cpp000066400000000000000000000133731425106661500217260ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/mainWindow/mainMenu.h" #include "core/const.h" #include "core/patch.h" #include "glue/layout.h" #include "glue/main.h" #include "gui/elems/basics/boxtypes.h" #include "gui/elems/basics/button.h" #include "gui/ui.h" #include "keyboard/keyboard.h" #include "utils/gui.h" #include extern giada::v::Ui g_ui; namespace giada::v { namespace { enum class FileMenu { OPEN_PROJECT = 0, SAVE_PROJECT, CLOSE_PROJECT, #ifdef G_DEBUG_MODE DEBUG_STATS, #endif QUIT }; void fileMenuCallback(Fl_Widget* /*w*/, void* v) { switch ((FileMenu)(intptr_t)v) { case FileMenu::OPEN_PROJECT: c::layout::openBrowserForProjectLoad(); break; case FileMenu::SAVE_PROJECT: c::layout::openBrowserForProjectSave(); break; case FileMenu::CLOSE_PROJECT: c::main::closeProject(); break; #ifdef G_DEBUG_MODE case FileMenu::DEBUG_STATS: c::main::printDebugInfo(); break; #endif case FileMenu::QUIT: c::main::quitGiada(); break; } } /* -------------------------------------------------------------------------- */ enum class EditMenu { FREE_SAMPLE_CHANNELS = 0, CLEAR_ALL_ACTIONS, SETUP_MIDI_INPUT }; void editMenuCallback(Fl_Widget* /*w*/, void* v) { switch ((EditMenu)(intptr_t)v) { case EditMenu::FREE_SAMPLE_CHANNELS: c::main::clearAllSamples(); break; case EditMenu::CLEAR_ALL_ACTIONS: c::main::clearAllActions(); break; case EditMenu::SETUP_MIDI_INPUT: c::layout::openMasterMidiInputWindow(); break; } } } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ geMainMenu::geMainMenu() : geFlex(Direction::HORIZONTAL, G_GUI_INNER_MARGIN) { geButton* file = new geButton(g_ui.langMapper.get(LangMap::MAIN_MENU_FILE)); geButton* edit = new geButton(g_ui.langMapper.get(LangMap::MAIN_MENU_EDIT)); geButton* config = new geButton(g_ui.langMapper.get(LangMap::MAIN_MENU_CONFIG)); geButton* about = new geButton(g_ui.langMapper.get(LangMap::MAIN_MENU_ABOUT)); add(file, 80); add(edit, 80); add(config, 80); add(about, 80); end(); file->onClick = [this]() { cb_file(); }; edit->onClick = [this]() { cb_edit(); }; about->onClick = []() { c::layout::openAboutWindow(); }; config->onClick = []() { c::layout::openConfigWindow(); }; } /* -------------------------------------------------------------------------- */ void geMainMenu::cb_file() { Fl_Menu_Item menu[] = { u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_MENU_FILE_OPENPROJECT), fileMenuCallback, (void*)FileMenu::OPEN_PROJECT), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_MENU_FILE_SAVEPROJECT), fileMenuCallback, (void*)FileMenu::SAVE_PROJECT), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_MENU_FILE_CLOSEPROJECT), fileMenuCallback, (void*)FileMenu::CLOSE_PROJECT), #ifdef G_DEBUG_MODE u::gui::makeMenuItem("Debug stats", fileMenuCallback, (void*)FileMenu::DEBUG_STATS), #endif u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_MENU_FILE_QUIT), fileMenuCallback, (void*)FileMenu::QUIT), {}}; Fl_Menu_Button b(0, 0, 100, 50); b.box(G_CUSTOM_BORDER_BOX); b.textsize(G_GUI_FONT_SIZE_BASE); b.textcolor(G_COLOR_LIGHT_2); b.color(G_COLOR_GREY_2); const Fl_Menu_Item* m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, &b); if (m != nullptr) m->do_callback(this, m->user_data()); } /* -------------------------------------------------------------------------- */ void geMainMenu::cb_edit() { c::main::MainMenu menu = c::main::getMainMenu(); Fl_Menu_Item menuItem[] = { u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_MENU_EDIT_FREEALLSAMPLES), editMenuCallback, (void*)EditMenu::FREE_SAMPLE_CHANNELS), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_MENU_EDIT_CLEARALLACTIONS), editMenuCallback, (void*)EditMenu::CLEAR_ALL_ACTIONS), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::MAIN_MENU_EDIT_SETUPMIDIINPUT), editMenuCallback, (void*)EditMenu::SETUP_MIDI_INPUT), {}}; menuItem[(int)EditMenu::FREE_SAMPLE_CHANNELS].deactivate(); menuItem[(int)EditMenu::CLEAR_ALL_ACTIONS].deactivate(); if (menu.hasAudioData) menuItem[(int)EditMenu::FREE_SAMPLE_CHANNELS].activate(); if (menu.hasActions) menuItem[(int)EditMenu::CLEAR_ALL_ACTIONS].activate(); Fl_Menu_Button b(0, 0, 100, 50); b.box(G_CUSTOM_BORDER_BOX); b.textsize(G_GUI_FONT_SIZE_BASE); b.textcolor(G_COLOR_LIGHT_2); b.color(G_COLOR_GREY_2); const Fl_Menu_Item* m = menuItem->popup(Fl::event_x(), Fl::event_y(), 0, 0, &b); if (m != nullptr) m->do_callback(this, m->user_data()); } } // namespace giada::vgiada-0.22.0/src/gui/elems/mainWindow/mainMenu.h000066400000000000000000000025461425106661500213730ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_MAIN_MENU_H #define GE_MAIN_MENU_H #include "gui/elems/basics/flex.h" namespace giada::v { class geMainMenu : public geFlex { public: geMainMenu(); private: void cb_file(); void cb_edit(); }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/mainWindow/mainTimer.cpp000066400000000000000000000111371425106661500220760ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "mainTimer.h" #include "core/const.h" #include "core/graphics.h" #include "glue/events.h" #include "glue/layout.h" #include "glue/main.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/choice.h" #include "gui/ui.h" #include "utils/gui.h" #include "utils/string.h" extern giada::v::Ui g_ui; namespace giada::v { geMainTimer::geMainTimer() : geFlex(Direction::HORIZONTAL, G_GUI_INNER_MARGIN) { m_bpm = new geButton(); m_meter = new geButton(); m_quantizer = new geChoice(); m_multiplier = new geButton("", multiplyOff_xpm, multiplyOn_xpm); m_divider = new geButton("", divideOff_xpm, divideOn_xpm); add(m_quantizer, 60); add(m_bpm, 60); add(m_meter, 60); add(m_multiplier, G_GUI_UNIT); add(m_divider, G_GUI_UNIT); end(); m_bpm->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_TIMER_LABEL_BPM)); m_meter->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_TIMER_LABEL_METER)); m_quantizer->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_TIMER_LABEL_QUANTIZER)); m_multiplier->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_TIMER_LABEL_MULTIPLIER)); m_divider->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_TIMER_LABEL_DIVIDER)); m_bpm->onClick = [&bpm = m_bpm]() { c::layout::openBpmWindow(bpm->label()); }; m_meter->onClick = [&timer = m_timer]() { c::layout::openBeatsWindow(timer.beats, timer.bars); }; m_multiplier->onClick = []() { c::events::multiplyBeats(); }; m_divider->onClick = []() { c::events::divideBeats(); }; m_quantizer->addItem(g_ui.langMapper.get(LangMap::COMMON_OFF)); m_quantizer->addItem("1\\/1"); m_quantizer->addItem("1\\/2"); m_quantizer->addItem("1\\/3"); m_quantizer->addItem("1\\/4"); m_quantizer->addItem("1\\/6"); m_quantizer->addItem("1\\/8"); m_quantizer->showItem(1); // "off" by default m_quantizer->onChange = [](ID value) { c::main::quantize(value); }; } /* -------------------------------------------------------------------------- */ void geMainTimer::refresh() { m_timer = c::main::getTimer(); if (m_timer.isRecordingInput) { 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::rebuild() { m_timer = c::main::getTimer(); setBpm(m_timer.bpm); setMeter(m_timer.beats, m_timer.bars); setQuantizer(m_timer.quantize); } /* -------------------------------------------------------------------------- */ void geMainTimer::setBpm(const char* v) { m_bpm->copy_label(v); } void geMainTimer::setBpm(float v) { 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(); } } /* -------------------------------------------------------------------------- */ void geMainTimer::setQuantizer(int q) { m_quantizer->showItem(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()); } } // namespace giada::v giada-0.22.0/src/gui/elems/mainWindow/mainTimer.h000066400000000000000000000034071425106661500215440ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_MAIN_TIMER_H #define GE_MAIN_TIMER_H #include "glue/main.h" #include "gui/elems/basics/flex.h" namespace giada::v { class geButton; class geChoice; class geMainTimer : public geFlex { public: geMainTimer(); void refresh(); void rebuild(); void setBpm(const char* v); void setBpm(float v); void setMeter(int beats, int bars); void setQuantizer(int q); /* setLock Locks bpm, meter and multipliers. Used during audio recordings. */ void setLock(bool v); private: c::main::Timer m_timer; geButton* m_bpm; geButton* m_meter; geChoice* m_quantizer; geButton* m_multiplier; geButton* m_divider; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/mainWindow/mainTransport.cpp000066400000000000000000000102631425106661500230110ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/mainWindow/mainTransport.h" #include "core/conf.h" #include "core/const.h" #include "core/graphics.h" #include "glue/events.h" #include "glue/main.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/flex.h" #include "gui/elems/basics/statusButton.h" #include "gui/ui.h" extern giada::v::Ui g_ui; namespace giada::v { geMainTransport::geMainTransport() : geFlex(Direction::HORIZONTAL, G_GUI_INNER_MARGIN) { m_rewind = new geButton("", rewindOff_xpm, rewindOn_xpm); m_play = new geStatusButton(play_xpm, pause_xpm); m_recTriggerMode = new geStatusButton(recTriggerModeOff_xpm, recTriggerModeOn_xpm); m_recAction = new geStatusButton(recOff_xpm, recOn_xpm); m_recInput = new geStatusButton(inputRecOff_xpm, inputRecOn_xpm); m_inputRecMode = new geStatusButton(freeInputRecOff_xpm, freeInputRecOn_xpm); m_metronome = new geStatusButton(metronomeOff_xpm, metronomeOn_xpm); add(m_rewind, 25); add(m_play, 25); add(new geBox(), 10); add(m_recTriggerMode, 15); add(m_recAction, 25); add(m_recInput, 25); add(m_inputRecMode, 15); add(new geBox(), 10); add(m_metronome, 15); end(); m_rewind->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_TRANSPORT_LABEL_REWIND)); m_play->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_TRANSPORT_LABEL_PLAY)); m_recTriggerMode->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_TRANSPORT_LABEL_RECTRIGGERMODE)); m_recAction->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_TRANSPORT_LABEL_RECACTIONS)); m_recInput->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_TRANSPORT_LABEL_RECINPUT)); m_inputRecMode->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_TRANSPORT_LABEL_RECINPUTMODE)); m_metronome->copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_TRANSPORT_LABEL_METRONOME)); m_rewind->callback([](Fl_Widget* /*w*/, void* /*v*/) { c::events::rewindSequencer(Thread::MAIN); }); m_play->callback([](Fl_Widget* /*w*/, void* /*v*/) { c::events::toggleSequencer(Thread::MAIN); }); m_recAction->callback([](Fl_Widget* /*w*/, void* /*v*/) { c::events::toggleActionRecording(); }); m_recInput->callback([](Fl_Widget* /*w*/, void* /*v*/) { c::events::toggleInputRecording(); }); m_recTriggerMode->callback([](Fl_Widget* /*w*/, void* /*v*/) { c::main::toggleRecOnSignal(); }); 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() { c::main::Transport transport = c::main::getTransport(); m_play->setStatus(transport.isRunning); m_recAction->setStatus(transport.isRecordingAction); m_recInput->setStatus(transport.isRecordingInput); m_metronome->setStatus(transport.isMetronomeOn); m_recTriggerMode->setStatus(transport.recTriggerMode == RecTriggerMode::SIGNAL); m_inputRecMode->setStatus(transport.inputRecMode == InputRecMode::FREE); } } // namespace giada::v giada-0.22.0/src/gui/elems/mainWindow/mainTransport.h000066400000000000000000000031411425106661500224530ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_MAIN_TRANSPORT_H #define GE_MAIN_TRANSPORT_H #include "gui/elems/basics/flex.h" namespace giada::v { class geButton; class geStatusButton; class geMainTransport : public geFlex { public: geMainTransport(); void refresh(); private: geButton* m_rewind; geStatusButton* m_play; geStatusButton* m_recTriggerMode; geStatusButton* m_recAction; geStatusButton* m_recInput; geStatusButton* m_inputRecMode; geStatusButton* m_metronome; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/mainWindow/sequencer.cpp000066400000000000000000000071531425106661500221460ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * beatMeter * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "sequencer.h" #include "core/const.h" #include "gui/drawing.h" #include "gui/ui.h" #include "utils/math.h" #include extern giada::v::Ui g_ui; namespace giada::v { geSequencer::geSequencer() : geBox() { copy_tooltip(g_ui.langMapper.get(LangMap::MAIN_SEQUENCER_LABEL)); } /* -------------------------------------------------------------------------- */ 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 giada-0.22.0/src/gui/elems/mainWindow/sequencer.h000066400000000000000000000033651425106661500216140ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * beatMeter * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_SEQUENCER_H #define GE_SEQUENCER_H #include "core/types.h" #include "deps/geompp/src/rect.hpp" #include "glue/main.h" #include "gui/elems/basics/box.h" namespace giada::v { class geSequencer : public geBox { public: geSequencer(); 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 m_background; geompp::Rect m_cell; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/midiIO/000077500000000000000000000000001425106661500165005ustar00rootroot00000000000000giada-0.22.0/src/gui/elems/midiIO/midiLearner.cpp000066400000000000000000000064261425106661500214470ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/midiIO/midiLearner.h" #include "core/const.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/boxtypes.h" #include "gui/elems/basics/button.h" #include "gui/ui.h" #include "utils/string.h" #include extern giada::v::Ui g_ui; namespace giada::v { geMidiLearner::geMidiLearner(int x, int y, int w, int h, std::string l, int param) : geFlex(x, y, w, h, Direction::HORIZONTAL, G_GUI_INNER_MARGIN) , onStartLearn(nullptr) , onStopLearn(nullptr) , onClearLearn(nullptr) , m_param(param) { m_text = new geBox(l.c_str()); m_valueBtn = new geButton(); m_button = new geButton(g_ui.langMapper.get(LangMap::COMMON_LEARN)); add(m_text); add(m_valueBtn, 80); add(m_button, 50); end(); m_text->box(G_CUSTOM_BORDER_BOX); m_valueBtn->box(G_CUSTOM_BORDER_BOX); m_valueBtn->when(FL_WHEN_RELEASE); m_valueBtn->onClick = [this]() { assert(onClearLearn != nullptr); if (Fl::event_button() == FL_RIGHT_MOUSE) onClearLearn(m_param); }; m_button->type(FL_TOGGLE_BUTTON); m_button->onClick = [this]() { assert(onStartLearn != nullptr); assert(onStopLearn != nullptr); if (m_button->value() == 1) onStartLearn(m_param); else onStopLearn(); }; } /* -------------------------------------------------------------------------- */ void geMidiLearner::update(uint32_t value) { std::string tmp = g_ui.langMapper.get(LangMap::COMMON_NOTSET); 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 } m_valueBtn->copy_label(tmp.c_str()); m_button->value(0); } /* -------------------------------------------------------------------------- */ void geMidiLearner::update(const std::string& s) { m_valueBtn->copy_label(s.c_str()); m_button->value(0); } /* -------------------------------------------------------------------------- */ void geMidiLearner::activate() { Fl_Group::activate(); m_valueBtn->activate(); m_button->activate(); } void geMidiLearner::deactivate() { Fl_Group::deactivate(); m_valueBtn->deactivate(); m_button->deactivate(); } } // namespace giada::vgiada-0.22.0/src/gui/elems/midiIO/midiLearner.h000066400000000000000000000037421425106661500211120ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_MIDI_LEARNER_H #define GE_MIDI_LEARNER_H #include "gui/elems/basics/flex.h" #include #include namespace giada::v { class geBox; class geButton; class geMidiLearner : public geFlex { public: geMidiLearner(int x, int y, int w, int h, std::string l, int param); /* update Updates and repaints the label widget with value 'value'. */ void update(uint32_t value); /* update (1) Just sets the label widget with a string value (no parsing done as in (1)). */ void update(const std::string&); void activate(); void deactivate(); std::function onStartLearn; std::function onStopLearn; std::function onClearLearn; protected: /* m_param Parameter index to be learnt. */ int m_param; geBox* m_text; geButton* m_valueBtn; geButton* m_button; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/midiIO/midiLearnerPack.cpp000066400000000000000000000050561425106661500222440ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "midiLearnerPack.h" #include "core/const.h" #include "glue/io.h" #include "gui/elems/basics/box.h" #include namespace giada::v { constexpr int LEARNER_WIDTH = 284; /* -------------------------------------------------------------------------- */ geMidiLearnerPack::geMidiLearnerPack(int X, int Y, std::string title) : gePack(X, Y, Direction::VERTICAL) { end(); 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 s, std::function c) { m_onStartLearn = s; m_onClearLearn = c; } /* -------------------------------------------------------------------------- */ void geMidiLearnerPack::addMidiLearner(std::string label, int param, bool visible) { geMidiLearner* l = new geMidiLearner(0, 0, LEARNER_WIDTH, G_GUI_UNIT, label, param); l->onStartLearn = m_onStartLearn; l->onClearLearn = m_onClearLearn; l->onStopLearn = []() { c::io::stopMidiLearn(); }; add(l); 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(); } } // namespace giada::vgiada-0.22.0/src/gui/elems/midiIO/midiLearnerPack.h000066400000000000000000000034061425106661500217060ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_LEARNER_PACK_H #define GE_LEARNER_PACK_H #include "gui/elems/basics/pack.h" #include "gui/elems/midiIO/midiLearner.h" #include #include namespace giada::v { class geMidiLearnerPack : public gePack { public: geMidiLearnerPack(int x, int y, std::string title = ""); void setCallbacks(std::function, std::function); void addMidiLearner(std::string label, int param, bool visible = true); void setEnabled(bool v); std::vector learners; private: std::function m_onStartLearn; std::function m_onClearLearn; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/plugin/000077500000000000000000000000001425106661500166245ustar00rootroot00000000000000giada-0.22.0/src/gui/elems/plugin/pluginBrowser.cpp000066400000000000000000000070001425106661500221670ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #include "gui/elems/plugin/pluginBrowser.h" #include "core/const.h" #include "core/plugins/pluginManager.h" #include "glue/plugin.h" #include "gui/elems/basics/boxtypes.h" #include "gui/ui.h" #include "utils/gui.h" #include extern giada::v::Ui g_ui; namespace giada::v { gePluginBrowser::gePluginBrowser(int x, int y, int w, int h) : Fl_Browser(x, y, w, h) , m_widths{0} { box(G_CUSTOM_BORDER_BOX); textsize(G_GUI_FONT_SIZE_BASE); textcolor(G_COLOR_LIGHT_2); selection_color(G_COLOR_GREY_4); color(G_COLOR_GREY_2); this->scrollbar.color(G_COLOR_GREY_2); this->scrollbar.selection_color(G_COLOR_GREY_4); this->scrollbar.labelcolor(G_COLOR_LIGHT_1); this->scrollbar.slider(G_CUSTOM_BORDER_BOX); this->hscrollbar.color(G_COLOR_GREY_2); this->hscrollbar.selection_color(G_COLOR_GREY_4); this->hscrollbar.labelcolor(G_COLOR_LIGHT_1); this->hscrollbar.slider(G_CUSTOM_BORDER_BOX); type(FL_HOLD_BROWSER); computeWidths(); column_widths(m_widths); column_char('\t'); // tabs as column delimiters refresh(); end(); } /* -------------------------------------------------------------------------- */ void gePluginBrowser::refresh() { clear(); add(g_ui.langMapper.get(LangMap::PLUGINCHOOSER_HEADER)); add("---\t---\t---\t---\t---"); for (m::PluginManager::PluginInfo pi : c::plugin::getPluginsInfo()) { std::string s; if (pi.isKnown) { std::string m = pi.exists ? "" : "@-"; s = fmt::format("{0}{1}\t{0}{2}\t{0}{3}\t{0}{4}\t{0}{5}", m, pi.name, pi.manufacturerName, pi.category, pi.format, pi.uid); } else s = fmt::format("?\t?\t?\t?\t? {} ?", pi.uid); add(s.c_str()); } } /* -------------------------------------------------------------------------- */ void gePluginBrowser::computeWidths() { constexpr int PADDDING = 60; for (m::PluginManager::PluginInfo pi : c::plugin::getPluginsInfo()) { // Explicit type std::max to fix MINMAX macro hell on Windows m_widths[0] = std::max(u::gui::getStringRect(pi.name).w, m_widths[0]); m_widths[1] = std::max(u::gui::getStringRect(pi.manufacturerName).w, m_widths[1]); m_widths[2] = std::max(u::gui::getStringRect(pi.category).w, m_widths[2]); m_widths[3] = std::max(u::gui::getStringRect(pi.format).w, m_widths[3]); } m_widths[0] += PADDDING; m_widths[1] += PADDDING; m_widths[2] += PADDDING; m_widths[3] += PADDDING; m_widths[4] = 0; } } // namespace giada::v #endif giada-0.22.0/src/gui/elems/plugin/pluginBrowser.h000066400000000000000000000027031425106661500216410ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #ifndef GE_PLUGIN_BROWSER_H #define GE_PLUGIN_BROWSER_H #include namespace giada::v { class gePluginBrowser : public Fl_Browser { public: gePluginBrowser(int x, int y, int w, int h); void refresh(); private: void computeWidths(); int m_widths[5]; }; } // namespace giada::v #endif #endif giada-0.22.0/src/gui/elems/plugin/pluginElement.cpp000066400000000000000000000136301425106661500221430ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #include "pluginElement.h" #include "core/graphics.h" #include "core/plugins/plugin.h" #include "core/plugins/pluginHost.h" #include "glue/plugin.h" #include "gui/dialogs/mainWindow.h" #include "gui/dialogs/pluginList.h" #include "gui/dialogs/pluginWindow.h" #include "gui/dialogs/pluginWindowGUI.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/choice.h" #include "gui/ui.h" #include "utils/gui.h" #include "utils/log.h" #include #include extern giada::v::Ui g_ui; namespace giada::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) , 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) { add(&button); add(&program); add(&bypass); add(&shiftUp); add(&shiftDown); add(&remove); resizable(button); remove.callback(cb_removePlugin, (void*)this); if (!m_plugin.valid) { button.copy_label(m_plugin.uniqueId.c_str()); button.deactivate(); bypass.deactivate(); shiftUp.deactivate(); shiftDown.deactivate(); return; } button.copy_label(m_plugin.name.c_str()); button.callback(cb_openPluginWindow, (void*)this); program.onChange = [pluginId = m_plugin.id](ID id) { c::plugin::setProgram(pluginId, id); }; for (const auto& p : m_plugin.programs) program.addItem(u::gui::removeFltkChars(p.name)); if (program.countItems() == 0) { program.addItem(g_ui.langMapper.get(LangMap::PLUGINLIST_NOPROGRAMS)); program.showItem(0); program.deactivate(); } else program.showItem(m_plugin.currentProgram); bypass.callback(cb_setBypass, (void*)this); bypass.type(FL_TOGGLE_BUTTON); bypass.value(m_plugin.isBypassed ? 0 : 1); shiftUp.callback(cb_shiftUp, (void*)this); shiftDown.callback(cb_shiftDown, (void*)this); } /* -------------------------------------------------------------------------- */ 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_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_shiftUp() { const gdPluginList* parent = static_cast(window()); c::plugin::swapPlugins(m_plugin.getPluginRef(), parent->getPrevElement(*this).getPluginRef(), m_plugin.channelId); } /* -------------------------------------------------------------------------- */ void gePluginElement::cb_shiftDown() { const gdPluginList* parent = static_cast(window()); 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(window())->delSubWindow(m_plugin.id + 1); 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 the parent window 'add plugin'. */ const int pwid = m_plugin.id + 1; gdWindow* parent = static_cast(window()); gdWindow* child = parent->getChild(pwid); /* If Plug-in window is already opened, just raise it on top and quit. */ if (child != nullptr) { child->show(); return; } if (m_plugin.hasEditor) child = new gdPluginWindowGUI(m_plugin); else child = new gdPluginWindow(m_plugin); child->setId(pwid); parent->addSubWindow(child); } /* -------------------------------------------------------------------------- */ void gePluginElement::cb_setBypass() { c::plugin::toggleBypass(m_plugin.id); } } // namespace giada::v #endif // #ifdef WITH_VST giada-0.22.0/src/gui/elems/plugin/pluginElement.h000066400000000000000000000042251425106661500216100ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #ifndef GE_PLUGIN_ELEMENT_H #define GE_PLUGIN_ELEMENT_H #include "glue/plugin.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/choice.h" #include "gui/elems/basics/pack.h" namespace giada::v { class gePluginElement : public gePack { public: gePluginElement(int x, int y, c::plugin::Plugin); ID getPluginId() const; const m::Plugin& getPluginRef() const; geButton button; geChoice program; geButton bypass; geButton shiftUp; geButton shiftDown; geButton remove; 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); void cb_removePlugin(); void cb_openPluginWindow(); void cb_setBypass(); void cb_shiftUp(); void cb_shiftDown(); c::plugin::Plugin m_plugin; }; } // namespace giada::v #endif #endif // #ifdef WITH_VST giada-0.22.0/src/gui/elems/plugin/pluginParameter.cpp000066400000000000000000000056161425106661500224770ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #include "pluginParameter.h" #include "core/const.h" #include "glue/events.h" #include "glue/plugin.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/boxtypes.h" #include "gui/elems/basics/slider.h" 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) { begin(); 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_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); 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(0, m_param.pluginId, m_param.index, m_slider->value(), Thread::MAIN); } /* -------------------------------------------------------------------------- */ 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); } } // namespace v } // namespace giada #endif // #ifdef WITH_VST giada-0.22.0/src/gui/elems/plugin/pluginParameter.h000066400000000000000000000033741425106661500221430ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifdef WITH_VST #ifndef GE_PLUGIN_PARAMETER_H #define GE_PLUGIN_PARAMETER_H #include "core/types.h" #include "glue/plugin.h" #include class geSlider; namespace giada::v { class geBox; 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: static void cb_setValue(Fl_Widget* /*w*/, void* p); void cb_setValue(); const c::plugin::Param m_param; geBox* m_label; geSlider* m_slider; geBox* m_value; }; } // namespace giada::v #endif #endif // #ifdef WITH_VST giada-0.22.0/src/gui/elems/sampleEditor/000077500000000000000000000000001425106661500177565ustar00rootroot00000000000000giada-0.22.0/src/gui/elems/sampleEditor/panTool.cpp000066400000000000000000000064761425106661500221130ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "panTool.h" #include "core/const.h" #include "core/model/model.h" #include "core/waveFx.h" #include "glue/events.h" #include "gui/dialogs/sampleEditor.h" #include "gui/ui.h" #include "utils/gui.h" #include "utils/math.h" #include "utils/string.h" #include "waveTools.h" #include extern giada::v::Ui g_ui; namespace giada::v { gePanTool::gePanTool(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, g_ui.langMapper.get(LangMap::SAMPLEEDITOR_PAN), FL_ALIGN_LEFT) , 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, g_ui.langMapper.get(LangMap::COMMON_RESET)) { add(&m_label); add(&m_dial); add(&m_input); add(&m_reset); m_dial.range(0.0f, G_MAX_PAN); m_dial.callback(cb_panning, (void*)this); m_input.align(FL_ALIGN_RIGHT); m_input.readonly(1); m_input.cursor_color(FL_WHITE); m_reset.callback(cb_panReset, (void*)this); 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"; m_input.value(tmp.c_str()); } else if (v == 0.5) m_input.value("C"); 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_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); } } // namespace giada::vgiada-0.22.0/src/gui/elems/sampleEditor/panTool.h000066400000000000000000000036101425106661500215430ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_PAN_TOOL_H #define GE_PAN_TOOL_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/pack.h" 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); static void cb_panReset(Fl_Widget* /*w*/, void* p); void cb_panning(); void cb_panReset(); const c::sampleEditor::Data* m_data; geBox m_label; geDial m_dial; geInput m_input; geButton m_reset; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/sampleEditor/pitchTool.cpp000066400000000000000000000131601425106661500224300ustar00rootroot00000000000000 /* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "pitchTool.h" #include "core/const.h" #include "core/graphics.h" #include "core/model/model.h" #include "glue/events.h" #include "gui/dialogs/sampleEditor.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/ui.h" #include "utils/gui.h" #include "utils/string.h" #include extern giada::v::Ui g_ui; namespace giada::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, g_ui.langMapper.get(LangMap::SAMPLEEDITOR_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, g_ui.langMapper.get(LangMap::SAMPLEEDITOR_PITCH_TOBAR)) , m_pitchToSong(0, 0, 70, G_GUI_UNIT, g_ui.langMapper.get(LangMap::SAMPLEEDITOR_PITCH_TOSONG)) , 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, g_ui.langMapper.get(LangMap::COMMON_RESET)) { add(&m_label); add(&m_dial); add(&m_input); add(&m_pitchToBar); add(&m_pitchToSong); add(&m_pitchHalf); add(&m_pitchDouble); add(&m_pitchReset); m_dial.range(0.01f, 4.0f); m_dial.callback(cb_setPitch, (void*)this); m_dial.when(FL_WHEN_RELEASE); m_input.align(FL_ALIGN_RIGHT); m_input.callback(cb_setPitchNum, (void*)this); m_input.when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); m_pitchToBar.callback(cb_setPitchToBar, (void*)this); m_pitchToSong.callback(cb_setPitchToSong, (void*)this); m_pitchHalf.callback(cb_setPitchHalf, (void*)this); m_pitchDouble.callback(cb_setPitchDouble, (void*)this); m_pitchReset.callback(cb_resetPitch, (void*)this); 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 if (!isDial) 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_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_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_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); } /* -------------------------------------------------------------------------- */ void gePitchTool::cb_setPitchHalf() { 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); } /* -------------------------------------------------------------------------- */ void gePitchTool::cb_setPitchToBar() { c::events::setChannelPitch(m_data->channelId, m_data->end / (float)m_data->getFramesInBar(), Thread::MAIN); } /* -------------------------------------------------------------------------- */ void gePitchTool::cb_setPitchToSong() { c::events::setChannelPitch(m_data->channelId, m_data->end / (float)m_data->getFramesInLoop(), Thread::MAIN); } /* -------------------------------------------------------------------------- */ void gePitchTool::cb_resetPitch() { c::events::setChannelPitch(m_data->channelId, G_DEFAULT_PITCH, Thread::MAIN); } } // namespace giada::vgiada-0.22.0/src/gui/elems/sampleEditor/pitchTool.h000066400000000000000000000047251425106661500221040ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_PITCH_TOOL_H #define GE_PITCH_TOOL_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/pack.h" 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); 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_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(); const c::sampleEditor::Data* m_data; geBox m_label; geDial m_dial; geInput m_input; geButton m_pitchToBar; geButton m_pitchToSong; geButton m_pitchHalf; geButton m_pitchDouble; geButton m_pitchReset; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/sampleEditor/rangeTool.cpp000066400000000000000000000065351425106661500224250ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "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 "gui/ui.h" #include "utils/gui.h" #include "utils/string.h" #include "waveTools.h" #include #include extern giada::v::Ui g_ui; namespace giada::v { geRangeTool::geRangeTool(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, g_ui.langMapper.get(LangMap::SAMPLEEDITOR_RANGE), FL_ALIGN_LEFT) , m_begin(0, 0, 70, G_GUI_UNIT) , m_end(0, 0, 70, G_GUI_UNIT) , m_reset(0, 0, 70, G_GUI_UNIT, g_ui.langMapper.get(LangMap::COMMON_RESET)) { add(&m_label); add(&m_begin); add(&m_end); add(&m_reset); 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); m_reset.callback(cb_resetStartEnd, 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()); m_end.value(std::to_string(end).c_str()); } /* -------------------------------------------------------------------------- */ 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); } } // namespace giada::vgiada-0.22.0/src/gui/elems/sampleEditor/rangeTool.h000066400000000000000000000036431425106661500220670ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_RANGE_TOOL_H #define GE_RANGE_TOOL_H #include "core/types.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/input.h" #include "gui/elems/basics/pack.h" 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); static void cb_resetStartEnd(Fl_Widget* /*w*/, void* p); void cb_setChanPos(); void cb_resetStartEnd(); const c::sampleEditor::Data* m_data; geBox m_label; geInput m_begin; geInput m_end; geButton m_reset; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/sampleEditor/shiftTool.cpp000066400000000000000000000061321425106661500224370ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "shiftTool.h" #include "core/const.h" #include "core/model/model.h" #include "glue/sampleEditor.h" #include "gui/dialogs/sampleEditor.h" #include "gui/dialogs/warnings.h" #include "gui/ui.h" #include "utils/gui.h" #include "utils/string.h" #include #include extern giada::v::Ui g_ui; namespace giada::v { geShiftTool::geShiftTool(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, g_ui.langMapper.get(LangMap::SAMPLEEDITOR_SHIFT), FL_ALIGN_LEFT) , m_shift(0, 0, 70, G_GUI_UNIT) , m_reset(0, 0, 70, G_GUI_UNIT, g_ui.langMapper.get(LangMap::COMMON_RESET)) { add(&m_label); add(&m_shift); add(&m_reset); m_shift.type(FL_INT_INPUT); m_shift.when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY); // on focus lost or enter key m_shift.callback(cb_setShift, (void*)this); m_reset.callback(cb_reset, (void*)this); 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, f); } } // namespace giada::vgiada-0.22.0/src/gui/elems/sampleEditor/shiftTool.h000066400000000000000000000036101425106661500221020ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_SHIFT_TOOL_H #define GE_SHIFT_TOOL_H #include "core/types.h" #include "gui/elems/basics/box.h" #include "gui/elems/basics/button.h" #include "gui/elems/basics/input.h" #include "gui/elems/basics/pack.h" 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: 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 shift(int f); const c::sampleEditor::Data* m_data; geBox m_label; geInput m_shift; geButton m_reset; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/sampleEditor/volumeTool.cpp000066400000000000000000000063151425106661500226340ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "volumeTool.h" #include "core/const.h" #include "glue/events.h" #include "gui/dialogs/sampleEditor.h" #include "gui/elems/mainWindow/keyboard/channel.h" #include "gui/ui.h" #include "utils/gui.h" #include "utils/math.h" #include "utils/string.h" #include #include #include extern giada::v::Ui g_ui; namespace giada::v { geVolumeTool::geVolumeTool(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, g_ui.langMapper.get(LangMap::SAMPLEEDITOR_VOLUME), FL_ALIGN_LEFT) , 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); m_dial.range(0.0f, 1.0f); m_dial.callback(cb_setVolume, (void*)this); m_input.callback(cb_setVolumeNum, (void*)this); 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 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_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); } } // namespace giada::vgiada-0.22.0/src/gui/elems/sampleEditor/volumeTool.h000066400000000000000000000035721425106661500223030ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_VOLUME_TOOL_H #define GE_VOLUME_TOOL_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::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: static void cb_setVolume(Fl_Widget* /*w*/, void* p); static void cb_setVolumeNum(Fl_Widget* /*w*/, void* p); void cb_setVolume(); void cb_setVolumeNum(); const c::sampleEditor::Data* m_data; geBox m_label; geDial m_dial; geInput m_input; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/sampleEditor/waveTools.cpp000066400000000000000000000166201425106661500224520ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "waveTools.h" #include "core/const.h" #include "core/model/model.h" #include "core/waveFx.h" #include "glue/sampleEditor.h" #include "gui/dialogs/sampleEditor.h" #include "gui/elems/basics/boxtypes.h" #include "gui/ui.h" #include "utils/gui.h" #include "waveform.h" #include #include #include extern giada::v::Ui g_ui; namespace giada::v { namespace { enum class Menu { CUT = 0, COPY, PASTE, TRIM, SILENCE, REVERSE, NORMALIZE, FADE_IN, FADE_OUT, SMOOTH_EDGES, SET_BEGIN_END, TO_NEW_CHANNEL }; /* -------------------------------------------------------------------------- */ void menuCallback_(Fl_Widget* w, void* v) { const geWaveTools* wt = static_cast(w); 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; } } } // namespace /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ geWaveTools::geWaveTools(int x, int y, int w, int h, bool gridEnabled, int gridVal) : Fl_Scroll(x, y, w, h, nullptr) , m_data(nullptr) { type(Fl_Scroll::HORIZONTAL_ALWAYS); hscrollbar.color(G_COLOR_GREY_2); hscrollbar.selection_color(G_COLOR_GREY_4); hscrollbar.labelcolor(G_COLOR_LIGHT_1); hscrollbar.slider(G_CUSTOM_BORDER_BOX); waveform = new v::geWaveform(x, y, w, h - 24, gridEnabled, gridVal); } /* -------------------------------------------------------------------------- */ 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); waveform->rebuild(*m_data); } if (this->w() > waveform->w()) waveform->stretchToWindow(); int offset = waveform->x() + waveform->w() - this->w() - this->x(); if (offset < 0) 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(); return 1; } case FL_PUSH: { if (Fl::event_button3()) // right button { openMenu(); return 1; } Fl::focus(waveform); return Fl_Group::handle(e); } default: return Fl_Group::handle(e); } } /* -------------------------------------------------------------------------- */ void geWaveTools::openMenu() { Fl_Menu_Item menu[] = { u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::SAMPLEEDITOR_TOOLS_CUT), menuCallback_, (void*)Menu::CUT), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::SAMPLEEDITOR_TOOLS_COPY), menuCallback_, (void*)Menu::COPY), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::SAMPLEEDITOR_TOOLS_PASTE), menuCallback_, (void*)Menu::PASTE), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::SAMPLEEDITOR_TOOLS_TRIM), menuCallback_, (void*)Menu::TRIM), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::SAMPLEEDITOR_TOOLS_SILENCE), menuCallback_, (void*)Menu::SILENCE), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::SAMPLEEDITOR_TOOLS_REVERSE), menuCallback_, (void*)Menu::REVERSE), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::SAMPLEEDITOR_TOOLS_NORMALIZE), menuCallback_, (void*)Menu::NORMALIZE), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::SAMPLEEDITOR_TOOLS_FADE_IN), menuCallback_, (void*)Menu::FADE_IN), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::SAMPLEEDITOR_TOOLS_FADE_OUT), menuCallback_, (void*)Menu::FADE_OUT), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::SAMPLEEDITOR_TOOLS_SMOOTH_EDGES), menuCallback_, (void*)Menu::SMOOTH_EDGES), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::SAMPLEEDITOR_TOOLS_SET_BEGIN_END), menuCallback_, (void*)Menu::SET_BEGIN_END), u::gui::makeMenuItem(g_ui.langMapper.get(LangMap::SAMPLEEDITOR_TOOLS_TO_NEW_CHANNEL), menuCallback_, (void*)Menu::TO_NEW_CHANNEL), {}}; 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(); } Fl_Menu_Button b(0, 0, 100, 50); b.box(G_CUSTOM_BORDER_BOX); b.textsize(G_GUI_FONT_SIZE_BASE); b.textcolor(G_COLOR_LIGHT_2); b.color(G_COLOR_GREY_2); const Fl_Menu_Item* m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, &b); if (m != nullptr) m->do_callback(this, m->user_data()); return; } } // namespace giada::vgiada-0.22.0/src/gui/elems/sampleEditor/waveTools.h000066400000000000000000000041041425106661500221110ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_WAVE_TOOLS_H #define GE_WAVE_TOOLS_H #include 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, bool gridEnabled, int gridVal); void resize(int x, int y, int w, int h) override; int handle(int e) override; /* rebuild Updates the waveform by realloc-ing new data (i.e. when the waveform has changed). */ void rebuild(const c::sampleEditor::Data& d); /* refresh Redraws the waveform, called by the video thread. This is meant to be called repeatedly when you need to update the play head inside the waveform. The method is smart enough to skip painting if the channel is stopped. */ void refresh(); const c::sampleEditor::Data& getChannelData() const { return *m_data; } v::geWaveform* waveform; private: void openMenu(); const c::sampleEditor::Data* m_data; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/sampleEditor/waveform.cpp000066400000000000000000000375031425106661500223200ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/elems/sampleEditor/waveform.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 "gui/dialogs/sampleEditor.h" #include "gui/elems/basics/boxtypes.h" #include "utils/log.h" #include "waveTools.h" #include #include #include #include namespace giada::v { geWaveform::geWaveform(int x, int y, int w, int h, bool gridEnabled, int gridVal) : 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_waveform.size = w; m_grid.snap = gridEnabled; m_grid.level = gridVal; } /* -------------------------------------------------------------------------- */ void geWaveform::clearData() { m_waveform.sup.clear(); m_waveform.inf.clear(); m_waveform.size = 0; m_grid.points.clear(); } /* -------------------------------------------------------------------------- */ int geWaveform::alloc(int datasize, bool force) { const m::Wave& wave = m_data->getWaveRef(); 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.getBuffer().countFrames(); m_ratio = 1; } if (datasize == m_waveform.size && !force) return 0; clearData(); m_waveform.size = datasize; m_waveform.sup.resize(m_waveform.size); m_waveform.inf.resize(m_waveform.size); u::log::print("[geWaveform::alloc] %d pixels, %f m_ratio\n", m_waveform.size, m_ratio); int offset = h() / 2; int zero = y() + offset; // center, zero amplitude (-inf dB) /* 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.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++) { /* 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... 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... if (k >= wave.getBuffer().countFrames()) continue; /* Compute average of stereo signal. */ float avg = 0.0f; float* frame = wave.getBuffer()[k]; for (int j = 0; j < wave.getBuffer().countChannels(); j++) avg += frame[j]; avg /= wave.getBuffer().countChannels(); /* Find peaks (greater and lower). */ if (avg > peaksup) peaksup = avg; else if (avg <= peakinf) peakinf = avg; /* Fill up grid vector. */ if (gridFreq != 0 && (int)k % gridFreq == 0 && k != 0) m_grid.points.push_back(k); } m_waveform.sup[i] = zero - (peaksup * offset); m_waveform.inf[i] = zero - (peakinf * offset); // 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; } recalcPoints(); return 1; } /* -------------------------------------------------------------------------- */ void geWaveform::recalcPoints() { m_chanStart = m_data->begin; m_chanEnd = m_data->end; } /* -------------------------------------------------------------------------- */ void geWaveform::drawSelection() { if (!isSelected()) return; int a = frameToPixel(m_selection.a) + x(); int b = frameToPixel(m_selection.b) + x(); if (a < 0) a = 0; if (b >= w() + BORDER) b = w() + BORDER; if (a < b) fl_rectf(a, y(), b - a, h(), G_COLOR_GREY_4); else 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++) { 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]); } } /* -------------------------------------------------------------------------- */ 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) { int pp = frameToPixel(pf); if (pp > from && pp < to) 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); /* vertical line */ 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); else 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); /* vertical line */ fl_line(lineX, y() + 1, lineX, y() + h() - 2); 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); } /* -------------------------------------------------------------------------- */ void geWaveform::drawPlayHead() { int p = frameToPixel(m_data->a_getPreviewTracker()) + x(); fl_color(G_COLOR_LIGHT_2); 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 /* Draw things from 'from' (offset driven by the scrollbar) to 'to' (width of parent window). We don't draw the entire waveform, only the visible part. */ int from = abs(x() - parent()->x()); int to = from + parent()->w(); if (x() + w() < parent()->w()) to = x() + w() - BORDER; drawSelection(); drawWaveform(from, to); drawGrid(from, to); drawPlayHead(); fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4); // border box drawStartEndPoints(); } /* -------------------------------------------------------------------------- */ int geWaveform::handle(int e) { const m::Wave& wave = m_data->getWaveRef(); m_mouseX = pixelToFrame(Fl::event_x() - x()); m_mouseY = pixelToFrame(Fl::event_y() - y()); switch (e) { case FL_KEYDOWN: { if (Fl::event_key() == ' ') static_cast(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; } 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; } } return 1; } case FL_RELEASE: { c::sampleEditor::setPreviewTracker(m_mouseX); /* 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(); /* Handle begin/end markers interaction. */ 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; redraw(); 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_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); return 1; } case FL_DRAG: { /* here the mouse is on the m_chanStart tool */ if (m_chanStartLit && m_pushed) { m_chanStart = snap(m_mouseX); if (m_chanStart < 0) m_chanStart = 0; else if (m_chanStart >= m_chanEnd) m_chanStart = m_chanEnd - 2; redraw(); } else if (m_chanEndLit && m_pushed) { m_chanEnd = snap(m_mouseX); if (m_chanEnd > wave.getBuffer().countFrames()) m_chanEnd = wave.getBuffer().countFrames(); else if (m_chanEnd <= m_chanStart) m_chanEnd = m_chanStart + 2; redraw(); } /* Here the mouse is on the waveform, i.e. a new selection has started. */ else if (m_dragged) { m_selection.b = snap(m_mouseX); redraw(); } /* 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(); } 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) { if (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; } /* -------------------------------------------------------------------------- */ 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; } /* -------------------------------------------------------------------------- */ bool geWaveform::mouseOnSelectionA() const { int mouseXp = frameToPixel(m_mouseX); int selAp = frameToPixel(m_selection.a); return mouseXp >= selAp - (FLAG_WIDTH / 2) && mouseXp <= selAp + (FLAG_WIDTH / 2); } bool geWaveform::mouseOnSelectionB() const { int mouseXp = frameToPixel(m_mouseX); int selBp = frameToPixel(m_selection.b); return mouseXp >= selBp - (FLAG_WIDTH / 2) && mouseXp <= selBp + (FLAG_WIDTH / 2); } /* -------------------------------------------------------------------------- */ int geWaveform::pixelToFrame(int p) const { if (p <= 0) return 0; if (p > m_waveform.size) return m_data->waveSize - 1; 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 std::swap(m_selection.a, m_selection.b); c::sampleEditor::setPreviewTracker(m_selection.a); } /* -------------------------------------------------------------------------- */ void geWaveform::clearSelection() { m_selection.a = 0; m_selection.b = 0; } /* -------------------------------------------------------------------------- */ #ifdef G_OS_WINDOWS #undef IN #endif void geWaveform::setZoom(Zoom z) { 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; position(newX, y()); /* Avoid overflow when zooming out with scrollbar like that: |----------[scrollbar]| Offset vs smaller: |[wave------------| offset > 0 smaller = false |[wave----] | offset < 0, smaller = true |-------------] | offset < 0, smaller = false */ int parentW = parent()->w(); int thisW = x() + w() - BORDER; // visible width, not full width if (thisW < parentW) position(x() + parentW - thisW, y()); if (smaller()) stretchToWindow(); redraw(); } /* -------------------------------------------------------------------------- */ void geWaveform::stretchToWindow() { int s = parent()->w(); alloc(s); position(BORDER, y()); size(s, h()); } /* -------------------------------------------------------------------------- */ void geWaveform::rebuild(const c::sampleEditor::Data& d) { m_data = &d; clearSelection(); alloc(m_waveform.size, /*force=*/true); redraw(); } /* -------------------------------------------------------------------------- */ bool geWaveform::smaller() const { return w() < parent()->w(); } /* -------------------------------------------------------------------------- */ void geWaveform::setGridLevel(int l) { m_grid.points.clear(); m_grid.level = l; alloc(m_waveform.size, true); // force alloc 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::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(); } } // namespace giada::vgiada-0.22.0/src/gui/elems/sampleEditor/waveform.h000066400000000000000000000112571425106661500217630ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_WAVEFORM_H #define GE_WAVEFORM_H #include "core/const.h" #include "core/types.h" #include #include namespace giada::c::sampleEditor { struct Data; } namespace giada::v { class geWaveform : public Fl_Widget { public: #ifdef G_OS_WINDOWS /* Fuck... */ #undef IN #undef OUT #endif enum class Zoom { IN, OUT }; geWaveform(int x, int y, int w, int h, bool gridEnabled, int gridVal); void draw() override; int handle(int e) override; /* isSelected Tells whether a portion of the waveform has been selected. */ bool isSelected() const; int getSelectionA() const; int getSelectionB() const; bool getSnap() const; int getSize() const; /* recalcPoints Recomputes m_chanStart, m_chanEnd, ... */ void recalcPoints(); /* zoom Type == 1 : zoom out, type == -1: zoom in */ void setZoom(Zoom z); /* strecthToWindow Shrinks or enlarge the waveform to match parent's width (gWaveTools) */ void stretchToWindow(); /* rebuild Redraws the waveform. */ void rebuild(const c::sampleEditor::Data& d); /* setGridLevel Sets a new frequency level for the grid. 0 means disabled. */ void setGridLevel(int l); void setSnap(bool v); /* clearSelection Removes any active selection. */ void clearSelection(); /* setWaveId Call this when the Wave ID has changed (e.g. after a reload). */ void setWaveId(ID /*id*/){/* 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 SNAPPING = 16; /* selection Portion of the selected wave, in frames. */ struct { int a; int b; } m_selection; /* data Real graphic stuff from the underlying waveform. */ struct { std::vector sup; // upper part of the waveform std::vector inf; // lower part of the waveform int size; // width of the waveform to draw (in pixel) } m_waveform; struct { bool snap; int level; std::vector points; } m_grid; /* mouseOnStart/end Is mouse on start or end flag? */ bool mouseOnStart() const; bool mouseOnEnd() const; /* mouseOnSelectionA/B As above, for the selection. */ bool mouseOnSelectionA() const; bool mouseOnSelectionB() const; /* smaller Is the waveform smaller than the parent window? */ bool smaller() const; int pixelToFrame(int p) const; // TODO - move these to utils::, will be needed in actionEditor int frameToPixel(int f) const; // TODO - move these to utils::, will be needed in actionEditor /* fixSelection Helper function which flattens the selection if it was made from right to left (inverse selection). It also computes the absolute points. Call this one whenever the selection gesture is done. */ void fixSelection(); /* clearData Destroys any graphical buffer. */ void clearData(); /* snap Snaps a point at 'pos' pixel. */ int snap(int pos); /* draw* Drawing functions. */ void drawSelection(); void drawWaveform(int from, int to); void drawGrid(int from, int to); void drawStartEndPoints(); void drawPlayHead(); void selectAll(); /* alloc Allocates memory for the picture. It's smart enough not to reallocate if datasize hasn't changed, but it can be forced otherwise. */ int alloc(int datasize, bool force = false); const c::sampleEditor::Data* m_data; int m_chanStart; bool m_chanStartLit; int m_chanEnd; bool m_chanEndLit; bool m_pushed; bool m_dragged; bool m_resizedA; bool m_resizedB; float m_ratio; int m_mouseX; int m_mouseY; }; } // namespace giada::v #endif giada-0.22.0/src/gui/elems/soundMeter.cpp000066400000000000000000000064231425106661500201640ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "soundMeter.h" #include "core/const.h" #include "core/kernelAudio.h" #include "core/types.h" #include "gui/drawing.h" #include "utils/math.h" #include #include #include namespace giada::v { 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 /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ float geSoundMeter::Meter::compute(float peak) { /* dBFS (full scale) calculation, plus decay of -2dB per call. */ float dbLevelCur = u::math::linearToDB(std::fabs(peak)); if (dbLevelCur < m_dbLevelOld && m_dbLevelOld > -G_MIN_DB_SCALE) dbLevelCur = m_dbLevelOld - 2.0f; m_dbLevelOld = dbLevelCur; return dbLevelCur; } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ geSoundMeter::geSoundMeter(int x, int y, int w, int h, const char* l) : Fl_Box(x, y, w, h, l) { } /* -------------------------------------------------------------------------- */ void geSoundMeter::draw() { const geompp::Rect outline(x(), y(), w(), h()); const geompp::Rect body(outline.reduced(1)); drawRect(outline, G_COLOR_GREY_4); if (!ready) { drawRectf(body, G_COLOR_BLUE); return; } drawRectf(body, G_COLOR_GREY_2); // Cleanup const float dbL = m_left.compute(peak.left); const float dbR = m_right.compute(peak.right); const int colorL = std::fabs(peak.left) > 1.0f ? G_COLOR_BLUE : G_COLOR_GREY_4; const int colorR = std::fabs(peak.right) > 1.0f ? G_COLOR_BLUE : G_COLOR_GREY_4; const geompp::Rect bodyL(body.withTrimmedBottom(h() / 2)); const geompp::Rect bodyR(body.withTrimmedTop(h() / 2)); drawRectf(bodyL.withW(dbToPx_(dbL, w() - 2)), colorL); drawRectf(bodyR.withW(dbToPx_(dbR, w() - 2)), colorR); } } // namespace giada::vgiada-0.22.0/src/gui/elems/soundMeter.h000066400000000000000000000031351425106661500176260ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef GE_SOUND_METER_H #define GE_SOUND_METER_H #include "core/types.h" #include namespace giada::v { class geSoundMeter : public Fl_Box { public: geSoundMeter(int x, int y, int w, int h, const char* l = 0); void draw() override; Peak peak; // Peak from Mixer bool ready; // Kernel state private: class Meter { public: float compute(float peak); private: float m_dbLevelOld = 0.0f; }; Meter m_left; Meter m_right; }; } // namespace giada::v #endif giada-0.22.0/src/gui/langMapper.cpp000066400000000000000000000464061425106661500170250ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/langMapper.h" #include "utils/fs.h" #include "utils/log.h" #include #include namespace nl = nlohmann; namespace giada::v { LangMap::LangMap() : m_default("[Not set]") { m_data[COMMON_OK] = "Ok"; m_data[COMMON_CANCEL] = "Cancel"; m_data[COMMON_YES] = "Yes"; m_data[COMMON_NO] = "No"; m_data[COMMON_OFF] = "Off"; m_data[COMMON_SELECT] = "Select"; m_data[COMMON_LOAD] = "Load"; m_data[COMMON_SAVE] = "Save"; m_data[COMMON_WARNING] = "Warning"; m_data[COMMON_CLOSE] = "Close"; m_data[COMMON_ADD] = "Add"; m_data[COMMON_CLEAR] = "Clear"; m_data[COMMON_RELOAD] = "Reload"; m_data[COMMON_RESET] = "Reset"; m_data[COMMON_ZOOMIN] = "Zoom in"; m_data[COMMON_ZOOMOUT] = "Zoom out"; m_data[COMMON_GRIDRES] = "Grid resolution"; m_data[COMMON_SNAPTOGRID] = "Snap to grid"; m_data[COMMON_BIND] = "Bind"; m_data[COMMON_LEARN] = "Learn"; m_data[COMMON_NOTSET] = "(not set)"; m_data[COMMON_NONE] = "None"; m_data[MESSAGE_MAIN_FREEALLSAMPLES] = "Free all Sample channels: are you sure?"; m_data[MESSAGE_MAIN_CLEARALLACTIONS] = "Clear all actions: are you sure?"; m_data[MESSAGE_MAIN_CLEARALLVOLUMEACTIONS] = "Clear all volume actions: are you sure?"; m_data[MESSAGE_MAIN_CLEARALLSTARTSTOPACTIONS] = "Clear all start/stop actions: are you sure?"; m_data[MESSAGE_MAIN_CLOSEPROJECT] = "Close project: are you sure?"; m_data[MESSAGE_INIT_WRONGSYSTEM] = "Your soundcard isn't configured correctly.\n" "Check the configuration and restart Giada."; m_data[MESSAGE_INIT_QUITGIADA] = "Quit Giada: are you sure?"; m_data[MESSAGE_CHANNEL_MULTICHANNOTSUPPORTED] = "Multichannel samples not supported."; m_data[MESSAGE_CHANNEL_CANTREADSAMPLE] = "Unable to read this sample."; m_data[MESSAGE_CHANNEL_PATHTOOLONG] = "File path too long."; m_data[MESSAGE_CHANNEL_NOFILESPECIFIED] = "No file specified."; m_data[MESSAGE_CHANNEL_LOADINGSAMPLES] = "Loading samples..."; m_data[MESSAGE_CHANNEL_LOADINGSAMPLESERROR] = "Some files weren't loaded successfully."; m_data[MESSAGE_CHANNEL_DELETE] = "Delete channel: are you sure?"; m_data[MESSAGE_CHANNEL_FREE] = "Free channel: are you sure?"; m_data[MESSAGE_STORAGE_PATCHUNREADABLE] = "This patch is unreadable."; m_data[MESSAGE_STORAGE_PATCHINVALID] = "This patch is not valid."; m_data[MESSAGE_STORAGE_PATCHUNSUPPORTED] = "This patch format is no longer supported."; m_data[MESSAGE_STORAGE_PROJECTEXISTS] = "Project exists: overwrite?"; m_data[MESSAGE_STORAGE_LOADINGPROJECT] = "Loading project..."; m_data[MESSAGE_STORAGE_LOADINGSAMPLE] = "Loading sample..."; m_data[MESSAGE_STORAGE_SAVINGPROJECT] = "Saving project..."; m_data[MESSAGE_STORAGE_SAVINGPROJECTERROR] = "Unable to save the project!"; m_data[MESSAGE_STORAGE_CHOOSEPROJECTNAME] = "Please choose a project name."; m_data[MESSAGE_STORAGE_CHOOSEFILENAME] = "Please choose a file name."; m_data[MESSAGE_STORAGE_FILEEXISTS] = "File exists: overwrite?"; m_data[MESSAGE_STORAGE_SAVINGFILEERROR] = "Unable to save this sample!"; m_data[MAIN_MENU_FILE] = "File"; m_data[MAIN_MENU_FILE_OPENPROJECT] = "Open project..."; m_data[MAIN_MENU_FILE_SAVEPROJECT] = "Save project..."; m_data[MAIN_MENU_FILE_CLOSEPROJECT] = "Close project"; m_data[MAIN_MENU_FILE_QUIT] = "Quit Giada"; m_data[MAIN_MENU_EDIT] = "Edit"; m_data[MAIN_MENU_EDIT_FREEALLSAMPLES] = "Free all Sample channels"; m_data[MAIN_MENU_EDIT_CLEARALLACTIONS] = "Clear all actions"; m_data[MAIN_MENU_EDIT_SETUPMIDIINPUT] = "Setup global MIDI input..."; m_data[MAIN_MENU_CONFIG] = "Config"; m_data[MAIN_MENU_ABOUT] = "About"; m_data[MAIN_IO_LABEL_OUTMETER] = "Main output meter"; m_data[MAIN_IO_LABEL_INMETER] = "Main input meter"; m_data[MAIN_IO_LABEL_OUTVOL] = "Main output volume"; m_data[MAIN_IO_LABEL_INVOL] = "Main input volume"; m_data[MAIN_IO_LABEL_INTOOUT] = "Stream linker\n\nConnects input to output to enable \"hear what you're playing\" mode."; m_data[MAIN_IO_LABEL_FXOUT] = "Main output plug-ins"; m_data[MAIN_IO_LABEL_FXIN] = "Main input plug-ins"; m_data[MAIN_TIMER_LABEL_BPM] = "Beats per minute (BPM)"; m_data[MAIN_TIMER_LABEL_METER] = "Beats and bars"; m_data[MAIN_TIMER_LABEL_QUANTIZER] = "Live quantizer"; m_data[MAIN_TIMER_LABEL_MULTIPLIER] = "Beat multiplier"; m_data[MAIN_TIMER_LABEL_DIVIDER] = "Beat divider"; m_data[MAIN_SEQUENCER_LABEL] = "Main sequencer"; m_data[MAIN_TRANSPORT_LABEL_REWIND] = "Rewind"; m_data[MAIN_TRANSPORT_LABEL_PLAY] = "Play/Stop"; m_data[MAIN_TRANSPORT_LABEL_RECTRIGGERMODE] = "Record-on-signal mode\n\nIf enabled, action " "and audio recording will start only when a signal (key press or audio) " "is detected."; m_data[MAIN_TRANSPORT_LABEL_RECACTIONS] = "Record actions"; m_data[MAIN_TRANSPORT_LABEL_RECINPUT] = "Record audio"; m_data[MAIN_TRANSPORT_LABEL_RECINPUTMODE] = "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_data[MAIN_TRANSPORT_LABEL_METRONOME] = "Metronome"; m_data[MAIN_COLUMN_BUTTON] = "Edit column"; m_data[MAIN_COLUMN_BUTTON_ADDSAMPLECHANNEL] = "Add Sample channel"; m_data[MAIN_COLUMN_BUTTON_ADDMIDICHANNEL] = "Add MIDI channel"; m_data[MAIN_COLUMN_BUTTON_REMOVE] = "Remove"; m_data[MAIN_CHANNEL_NOSAMPLE] = "-- no sample --"; m_data[MAIN_CHANNEL_SAMPLENOTFOUND] = "* file not found! *"; m_data[MAIN_CHANNEL_LABEL_PLAY] = "Play/stop"; m_data[MAIN_CHANNEL_LABEL_ARM] = "Arm for recording"; m_data[MAIN_CHANNEL_LABEL_STATUS] = "Progress bar"; m_data[MAIN_CHANNEL_LABEL_READACTIONS] = "Read actions\n\nToggles playback of pre-recorded " "actions (key press, key release, ...)."; m_data[MAIN_CHANNEL_LABEL_MODEBOX] = "Mode"; m_data[MAIN_CHANNEL_LABEL_MUTE] = "Mute"; m_data[MAIN_CHANNEL_LABEL_SOLO] = "Solo"; m_data[MAIN_CHANNEL_LABEL_FX] = "Plug-ins"; m_data[MAIN_CHANNEL_LABEL_VOLUME] = "Volume"; m_data[MAIN_CHANNEL_LABEL_MIDIACTIVITY] = "MIDI I/O activity\n\nNotifies MIDI messages sent (top) or " "received (bottom) by this channel."; m_data[MAIN_CHANNEL_MENU_INPUTMONITOR] = "Input monitor"; m_data[MAIN_CHANNEL_MENU_OVERDUBPROTECTION] = "Overdub protection"; m_data[MAIN_CHANNEL_MENU_LOADSAMPLE] = "Load new sample..."; m_data[MAIN_CHANNEL_MENU_EXPORTSAMPLE] = "Export sample to file..."; m_data[MAIN_CHANNEL_MENU_KEYBOARDINPUT] = "Setup keyboard input..."; m_data[MAIN_CHANNEL_MENU_MIDIINPUT] = "Setup MIDI input..."; m_data[MAIN_CHANNEL_MENU_MIDIOUTPUT] = "Setup MIDI output..."; m_data[MAIN_CHANNEL_MENU_EDITSAMPLE] = "Edit sample..."; m_data[MAIN_CHANNEL_MENU_EDITACTIONS] = "Edit actions..."; m_data[MAIN_CHANNEL_MENU_CLEARACTIONS] = "Clear actions"; m_data[MAIN_CHANNEL_MENU_CLEARACTIONS_ALL] = "All"; m_data[MAIN_CHANNEL_MENU_CLEARACTIONS_VOLUME] = "Volume"; m_data[MAIN_CHANNEL_MENU_CLEARACTIONS_STARTSTOP] = "Start/Stop"; m_data[MAIN_CHANNEL_MENU_RENAME] = "Rename"; m_data[MAIN_CHANNEL_MENU_CLONE] = "Clone"; m_data[MAIN_CHANNEL_MENU_FREE] = "Free"; m_data[MAIN_CHANNEL_MENU_DELETE] = "Delete"; m_data[MISSINGASSETS_INTRO] = "This project contains missing assets."; m_data[MISSINGASSETS_AUDIOFILES] = "Audio files not found in the project folder:"; m_data[MISSINGASSETS_PLUGINS] = "Audio plug-ins not found globally:"; m_data[PLUGINCHOOSER_TITLE] = "Available plugins"; m_data[PLUGINCHOOSER_HEADER] = "NAME\tMANUFACTURER\tCATEGORY\tFORMAT\tUID"; m_data[PLUGINCHOOSER_SORTBY] = "Sort by"; m_data[PLUGINCHOOSER_SORTBY_NAME] = "Name"; m_data[PLUGINCHOOSER_SORTBY_CATEGORY] = "Category"; m_data[PLUGINCHOOSER_SORTBY_MANIFACTURER] = "Manifacturer"; m_data[PLUGINCHOOSER_SORTBY_FORMAT] = "Format"; m_data[PLUGINLIST_TITLE_MASTEROUT] = "Master Out Plug-ins"; m_data[PLUGINLIST_TITLE_MASTERIN] = "Master In Plug-ins"; m_data[PLUGINLIST_TITLE_CHANNEL] = "Channel Plug-ins"; m_data[PLUGINLIST_ADDPLUGIN] = "-- add new plugin --"; m_data[PLUGINLIST_NOPROGRAMS] = "-- no programs --"; m_data[CHANNELNAME_TITLE] = "New channel name"; m_data[KEYGRABBER_TITLE] = "Key configuration"; m_data[KEYGRABBER_BODY] = "Press a key.\n\nCurrent binding: "; m_data[SAMPLEEDITOR_TITLE] = "Sample Editor"; m_data[SAMPLEEDITOR_RELOAD] = "Reload"; m_data[SAMPLEEDITOR_LOOP] = "Loop"; m_data[SAMPLEEDITOR_INFO] = "File: {}\nSize: {} frames\nDuration {} seconds\nBit depth: {}\nFrequency: {} Hz"; m_data[SAMPLEEDITOR_PAN] = "Pan"; m_data[SAMPLEEDITOR_PITCH] = "Pitch"; m_data[SAMPLEEDITOR_PITCH_TOBAR] = "To bar"; m_data[SAMPLEEDITOR_PITCH_TOSONG] = "To song"; m_data[SAMPLEEDITOR_RANGE] = "Range"; m_data[SAMPLEEDITOR_SHIFT] = "Shift"; m_data[SAMPLEEDITOR_VOLUME] = "Volume"; m_data[SAMPLEEDITOR_TOOLS_CUT] = "Cut"; m_data[SAMPLEEDITOR_TOOLS_COPY] = "Copy"; m_data[SAMPLEEDITOR_TOOLS_PASTE] = "Paste"; m_data[SAMPLEEDITOR_TOOLS_TRIM] = "Trim"; m_data[SAMPLEEDITOR_TOOLS_SILENCE] = "Silence"; m_data[SAMPLEEDITOR_TOOLS_REVERSE] = "Reverse"; m_data[SAMPLEEDITOR_TOOLS_NORMALIZE] = "Normalize"; m_data[SAMPLEEDITOR_TOOLS_FADE_IN] = "Fade in"; m_data[SAMPLEEDITOR_TOOLS_FADE_OUT] = "Fade out"; m_data[SAMPLEEDITOR_TOOLS_SMOOTH_EDGES] = "Smooth edges"; m_data[SAMPLEEDITOR_TOOLS_SET_BEGIN_END] = "Set begin/end here"; m_data[SAMPLEEDITOR_TOOLS_TO_NEW_CHANNEL] = "Copy to new channel"; m_data[ACTIONEDITOR_TITLE] = "Action Editor"; m_data[ACTIONEDITOR_VOLUME] = "Volume"; m_data[ACTIONEDITOR_KEYPRESS] = "Key press"; m_data[ACTIONEDITOR_KEYRELEASE] = "Key release"; m_data[ACTIONEDITOR_STOPSAMPLE] = "Stop sample"; m_data[ACTIONEDITOR_STARTSTOP] = "Start/stop"; m_data[ACTIONEDITOR_STARTSTOPDISABLED] = "Start/stop (disabled)"; m_data[ACTIONEDITOR_VELOCITY] = "Velocity"; m_data[ACTIONEDITOR_LABEL_ACTIONTYPE] = "Action type to add"; m_data[BROWSER_SHOWHIDDENFILES] = "Show hidden files"; m_data[BROWSER_OPENPROJECT] = "Open project"; m_data[BROWSER_SAVEPROJECT] = "Save project"; m_data[BROWSER_OPENSAMPLE] = "Open sample"; m_data[BROWSER_SAVESAMPLE] = "Save sample"; m_data[BROWSER_OPENPLUGINSDIR] = "Open plug-ins directory"; m_data[MIDIINPUT_MASTER_TITLE] = "MIDI Input Setup (global)"; m_data[MIDIINPUT_MASTER_ENABLE] = "Enable MIDI input"; m_data[MIDIINPUT_MASTER_LEARN_REWIND] = "Rewind"; m_data[MIDIINPUT_MASTER_LEARN_PLAYSTOP] = "Play/stop"; m_data[MIDIINPUT_MASTER_LEARN_ACTIONREC] = "Action recording"; m_data[MIDIINPUT_MASTER_LEARN_INPUTREC] = "Input recording"; m_data[MIDIINPUT_MASTER_LEARN_METRONOME] = "metronome"; m_data[MIDIINPUT_MASTER_LEARN_INVOLUME] = "Input volume"; m_data[MIDIINPUT_MASTER_LEARN_OUTVOLUME] = "Output volume"; m_data[MIDIINPUT_MASTER_LEARN_SEQDOUBLE] = "Sequencer ×2"; m_data[MIDIINPUT_MASTER_LEARN_SEQHALF] = "Sequencer ÷2"; m_data[MIDIINPUT_CHANNEL_TITLE] = "MIDI Input Setup"; m_data[MIDIINPUT_CHANNEL_ENABLE] = "Enable MIDI input"; m_data[MIDIINPUT_CHANNEL_VELOCITYDRIVESVOL] = "Velocity drives volume (Sample Channels)"; m_data[MIDIINPUT_CHANNEL_LEARN_CHANNEL] = "Channel"; m_data[MIDIINPUT_CHANNEL_LEARN_KEYPRESS] = "Key press"; m_data[MIDIINPUT_CHANNEL_LEARN_KEYREL] = "Key release"; m_data[MIDIINPUT_CHANNEL_LEARN_KEYKILL] = "Key kill"; m_data[MIDIINPUT_CHANNEL_LEARN_ARM] = "Arm"; m_data[MIDIINPUT_CHANNEL_LEARN_MUTE] = "Mute"; m_data[MIDIINPUT_CHANNEL_LEARN_SOLO] = "Solo"; m_data[MIDIINPUT_CHANNEL_LEARN_VOLUME] = "Volume"; m_data[MIDIINPUT_CHANNEL_LEARN_PITCH] = "Pitch"; m_data[MIDIINPUT_CHANNEL_LEARN_READACTIONS] = "Read actions"; m_data[MIDIOUTPUT_CHANNEL_TITLE] = "Midi Output Setup"; m_data[MIDIOUTPUT_CHANNEL_ENABLE] = "Enable MIDI output"; m_data[MIDIOUTPUT_CHANNEL_ENABLE_LIGHTNING] = "Enable MIDI lightning output"; m_data[MIDIOUTPUT_CHANNEL_LEARN_PLAYING] = "Playing"; m_data[MIDIOUTPUT_CHANNEL_LEARN_MUTE] = "Mute"; m_data[MIDIOUTPUT_CHANNEL_LEARN_SOLO] = "Solo"; m_data[ABOUT_TITLE] = "About Giada"; m_data[ABOUT_BODY] = "Version {} ({} build) {}\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"; m_data[ABOUT_BODY_VST] = "VST Plug-In Technology by Steinberg\n" "VST is a trademark of Steinberg\nMedia Technologies GmbH"; m_data[CONFIG_TITLE] = "Configuration"; m_data[CONFIG_RESTARTGIADA] = "Restart Giada for the changes to take effect."; m_data[CONFIG_AUDIO_TITLE] = "Audio"; m_data[CONFIG_AUDIO_SYSTEM] = "System"; m_data[CONFIG_AUDIO_BUFFERSIZE] = "Buffer size"; m_data[CONFIG_AUDIO_SAMPLERATE] = "Sample rate"; m_data[CONFIG_AUDIO_OUTPUTDEVICE] = "Output device"; m_data[CONFIG_AUDIO_OUTPUTCHANNELS] = "Output channels"; m_data[CONFIG_AUDIO_LIMITOUTPUT] = "Limit output"; m_data[CONFIG_AUDIO_INPUTDEVICE] = "Input device"; m_data[CONFIG_AUDIO_INPUTCHANNELS] = "Input channels"; m_data[CONFIG_AUDIO_RECTHRESHOLD] = "Rec threshold (dB)"; m_data[CONFIG_AUDIO_ENABLEINPUT] = "Enable Input"; m_data[CONFIG_AUDIO_RESAMPLING] = "Resampling"; m_data[CONFIG_AUDIO_RESAMPLING_SINCBEST] = "Sinc best quality (very slow)"; m_data[CONFIG_AUDIO_RESAMPLING_SINCMEDIUM] = "Sinc medium quality (slow)"; m_data[CONFIG_AUDIO_RESAMPLING_SINCBASIC] = "Sinc basic quality (medium)"; m_data[CONFIG_AUDIO_RESAMPLING_ZEROORDER] = "Zero Order Hold (fast)"; m_data[CONFIG_AUDIO_RESAMPLING_LINEAR] = "Linear (very fast)"; m_data[CONFIG_AUDIO_NODEVICESFOUND] = "-- no devices found --"; m_data[CONFIG_MIDI_TITLE] = "MIDI"; m_data[CONFIG_MIDI_SYSTEM] = "System"; m_data[CONFIG_MIDI_OUTPUTPORT] = "Output port"; m_data[CONFIG_MIDI_INPUTPORT] = "Input port"; m_data[CONFIG_MIDI_NOPORTSFOUND] = "-- no ports found --"; m_data[CONFIG_MIDI_OUTPUTMIDIMAP] = "Output MIDI Map"; m_data[CONFIG_MIDI_NOMIDIMAPSFOUND] = "(no MIDI maps available)"; m_data[CONFIG_MIDI_SYNC] = "Sync"; m_data[CONFIG_MIDI_LABEL_ENABLEOUT] = "Enable Output port"; m_data[CONFIG_MIDI_LABEL_ENABLEIN] = "Enable Input port"; m_data[CONFIG_BEHAVIORS_TITLE] = "Behaviors"; m_data[CONFIG_BEHAVIORS_CHANSSTOPONSEQHALT] = "Dynamic channels stop immediately when the sequencer is halted"; m_data[CONFIG_BEHAVIORS_TREATRECSASLOOPS] = "Treat one shot channels with actions as loops"; m_data[CONFIG_BEHAVIORS_INPUTMONITORDEFAULTON] = "New sample channels have input monitor on by default"; m_data[CONFIG_BEHAVIORS_OVERDUBPROTECTIONDEFAULTON] = "New sample channels have overdub protection on by default"; m_data[CONFIG_BINDINGS_TITLE] = "Key Bindings"; m_data[CONFIG_BINDINGS_PLAY] = "Play"; m_data[CONFIG_BINDINGS_REWIND] = "Rewind"; m_data[CONFIG_BINDINGS_RECORDACTIONS] = "Record actions"; m_data[CONFIG_BINDINGS_RECORDAUDIO] = "Record audio"; m_data[CONFIG_BINDINGS_EXIT] = "Exit"; m_data[CONFIG_MISC_TITLE] = "Misc"; m_data[CONFIG_MISC_DEBUGMESSAGES] = "Debug messages"; m_data[CONFIG_MISC_DEBUGMESSAGES_DISABLED] = "Disabled"; m_data[CONFIG_MISC_DEBUGMESSAGES_TOSTDOUT] = "To standard output"; m_data[CONFIG_MISC_DEBUGMESSAGES_TOFILE] = "To file"; m_data[CONFIG_MISC_TOOLTIPS] = "Tooltips"; m_data[CONFIG_MISC_TOOLTIPS_DISABLED] = "Disabled"; m_data[CONFIG_MISC_TOOLTIPS_ENABLED] = "Enabled"; m_data[CONFIG_MISC_LANGUAGE] = "Language file"; m_data[CONFIG_MISC_NOLANGUAGESFOUND] = "-- no language files found --"; m_data[CONFIG_PLUGINS_TITLE] = "Plug-ins"; m_data[CONFIG_PLUGINS_FOLDER] = "Plug-ins folder"; m_data[CONFIG_PLUGINS_SCANNING] = "Scan in progress ({}%). Please wait..."; m_data[CONFIG_PLUGINS_SCAN] = "Scan ({} found)"; m_data[CONFIG_PLUGINS_INVALIDPATH] = "Invalid path."; } const char* LangMap::get(const std::string& key) const { if (m_data.count(key) == 0) return m_default.c_str(); return m_data.at(key).c_str(); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ LangMapper::LangMapper() { m_mapsPath = u::fs::getLangMapsPath(); } /* -------------------------------------------------------------------------- */ void LangMapper::init() { Mapper::init(); u::log::print("[LangMapper::init] total langmaps found: %d\n", m_mapFiles.size()); } /* -------------------------------------------------------------------------- */ int LangMapper::read(const std::string& file) { std::optional res = Mapper::read(file); if (!res) return G_FILE_UNREADABLE; m_map.m_data = res.value().get(); return G_FILE_OK; } /* -------------------------------------------------------------------------- */ const char* LangMapper::get(const std::string& s) const { return m_map.get(s); } } // namespace giada::v giada-0.22.0/src/gui/langMapper.h000066400000000000000000000571241425106661500164710ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_LANGMAPPER_H #define G_LANGMAPPER_H #include "mapper.h" #include #include #include #include namespace giada::v { class LangMap { public: friend class LangMapper; using Data = std::unordered_map; static constexpr auto COMMON_OK = "common_ok"; static constexpr auto COMMON_CANCEL = "common_cancel"; static constexpr auto COMMON_YES = "common_yes"; static constexpr auto COMMON_NO = "common_no"; static constexpr auto COMMON_OFF = "common_off"; static constexpr auto COMMON_SELECT = "common_select"; static constexpr auto COMMON_LOAD = "common_load"; static constexpr auto COMMON_SAVE = "common_save"; static constexpr auto COMMON_WARNING = "common_warning"; static constexpr auto COMMON_CLOSE = "common_close"; static constexpr auto COMMON_ADD = "common_add"; static constexpr auto COMMON_CLEAR = "common_clear"; static constexpr auto COMMON_RELOAD = "common_reload"; static constexpr auto COMMON_RESET = "common_reset"; static constexpr auto COMMON_ZOOMIN = "common_zoomIn"; static constexpr auto COMMON_ZOOMOUT = "common_zoomOut"; static constexpr auto COMMON_GRIDRES = "common_gridRes"; static constexpr auto COMMON_SNAPTOGRID = "common_snapToGrid"; static constexpr auto COMMON_BIND = "common_bind"; static constexpr auto COMMON_LEARN = "common_learn"; static constexpr auto COMMON_NOTSET = "common_notSet"; static constexpr auto COMMON_NONE = "common_none"; static constexpr auto MESSAGE_MAIN_FREEALLSAMPLES = "message_main_freeAllSamples"; static constexpr auto MESSAGE_MAIN_CLEARALLACTIONS = "message_main_clearAllActions"; static constexpr auto MESSAGE_MAIN_CLEARALLVOLUMEACTIONS = "message_main_clearAllVolumeActions"; static constexpr auto MESSAGE_MAIN_CLEARALLSTARTSTOPACTIONS = "message_main_clearAllStartStopActions"; static constexpr auto MESSAGE_MAIN_CLOSEPROJECT = "message_main_closeProject"; static constexpr auto MESSAGE_INIT_WRONGSYSTEM = "message_init_wrongSystem"; static constexpr auto MESSAGE_INIT_QUITGIADA = "message_init_quitGiada"; static constexpr auto MESSAGE_CHANNEL_MULTICHANNOTSUPPORTED = "message_channel_multiChanNotSupported"; static constexpr auto MESSAGE_CHANNEL_CANTREADSAMPLE = "message_channel_cantReadSample"; static constexpr auto MESSAGE_CHANNEL_PATHTOOLONG = "message_channel_pathTooLong"; static constexpr auto MESSAGE_CHANNEL_NOFILESPECIFIED = "message_channel_noFileSpecified"; static constexpr auto MESSAGE_CHANNEL_LOADINGSAMPLES = "message_channel_loadingSamples"; static constexpr auto MESSAGE_CHANNEL_LOADINGSAMPLESERROR = "message_channel_loadingSamplesError"; static constexpr auto MESSAGE_CHANNEL_DELETE = "message_channel_delete"; static constexpr auto MESSAGE_CHANNEL_FREE = "message_channel_free"; static constexpr auto MESSAGE_STORAGE_PATCHUNREADABLE = "message_storage_patchUnreadable"; static constexpr auto MESSAGE_STORAGE_PATCHINVALID = "message_storage_patchInvalid"; static constexpr auto MESSAGE_STORAGE_PATCHUNSUPPORTED = "message_storage_patchUnsupported"; static constexpr auto MESSAGE_STORAGE_PROJECTEXISTS = "message_storage_projectExists"; static constexpr auto MESSAGE_STORAGE_LOADINGPROJECT = "message_storage_loadingProject"; static constexpr auto MESSAGE_STORAGE_LOADINGSAMPLE = "message_storage_loadingSample"; static constexpr auto MESSAGE_STORAGE_SAVINGPROJECT = "message_storage_savingProject"; static constexpr auto MESSAGE_STORAGE_SAVINGPROJECTERROR = "message_storage_savingProjectError"; static constexpr auto MESSAGE_STORAGE_CHOOSEPROJECTNAME = "message_storage_chooseProjectName"; static constexpr auto MESSAGE_STORAGE_CHOOSEFILENAME = "message_storage_chooseFileName"; static constexpr auto MESSAGE_STORAGE_FILEEXISTS = "message_storage_fileExists"; static constexpr auto MESSAGE_STORAGE_SAVINGFILEERROR = "message_storage_savingFileError"; static constexpr auto MAIN_MENU_FILE = "main_menu_file"; static constexpr auto MAIN_MENU_FILE_OPENPROJECT = "main_menu_file_openProject"; static constexpr auto MAIN_MENU_FILE_SAVEPROJECT = "main_menu_file_saveProject"; static constexpr auto MAIN_MENU_FILE_CLOSEPROJECT = "main_menu_file_closeProject"; static constexpr auto MAIN_MENU_FILE_QUIT = "main_menu_file_quit"; static constexpr auto MAIN_MENU_EDIT = "main_menu_edit"; static constexpr auto MAIN_MENU_EDIT_FREEALLSAMPLES = "main_menu_edit_freeAllSamples"; static constexpr auto MAIN_MENU_EDIT_CLEARALLACTIONS = "main_menu_edit_clearAllActions"; static constexpr auto MAIN_MENU_EDIT_SETUPMIDIINPUT = "main_menu_edit_setupMidiInput"; static constexpr auto MAIN_MENU_CONFIG = "main_menu_config"; static constexpr auto MAIN_MENU_ABOUT = "main_menu_about"; static constexpr auto MAIN_IO_LABEL_OUTMETER = "main_IO_label_outMeter"; static constexpr auto MAIN_IO_LABEL_INMETER = "main_IO_label_inMeter"; static constexpr auto MAIN_IO_LABEL_OUTVOL = "main_IO_label_outVol"; static constexpr auto MAIN_IO_LABEL_INVOL = "main_IO_label_inVol"; static constexpr auto MAIN_IO_LABEL_INTOOUT = "main_IO_label_inToOut"; static constexpr auto MAIN_IO_LABEL_FXOUT = "main_IO_label_fxOut"; static constexpr auto MAIN_IO_LABEL_FXIN = "main_IO_label_fxIn"; static constexpr auto MAIN_TIMER_LABEL_BPM = "main_mainTimer_label_bpm"; static constexpr auto MAIN_TIMER_LABEL_METER = "main_mainTimer_label_meter"; static constexpr auto MAIN_TIMER_LABEL_QUANTIZER = "main_mainTimer_label_quantizer"; static constexpr auto MAIN_TIMER_LABEL_MULTIPLIER = "main_mainTimer_label_multiplier"; static constexpr auto MAIN_TIMER_LABEL_DIVIDER = "main_mainTimer_label_divider"; static constexpr auto MAIN_SEQUENCER_LABEL = "main_sequencer_label"; static constexpr auto MAIN_TRANSPORT_LABEL_REWIND = "main_transport_label_rewind"; static constexpr auto MAIN_TRANSPORT_LABEL_PLAY = "main_transport_label_play"; static constexpr auto MAIN_TRANSPORT_LABEL_RECTRIGGERMODE = "main_transport_label_recTriggerMode"; static constexpr auto MAIN_TRANSPORT_LABEL_RECACTIONS = "main_transport_label_recActions"; static constexpr auto MAIN_TRANSPORT_LABEL_RECINPUT = "main_transport_label_recInput"; static constexpr auto MAIN_TRANSPORT_LABEL_RECINPUTMODE = "main_transport_label_recInputMode"; static constexpr auto MAIN_TRANSPORT_LABEL_METRONOME = "main_transport_label_metronome"; static constexpr auto MAIN_COLUMN_BUTTON = "main_column_button"; static constexpr auto MAIN_COLUMN_BUTTON_ADDSAMPLECHANNEL = "main_column_button_addSampleChannel"; static constexpr auto MAIN_COLUMN_BUTTON_ADDMIDICHANNEL = "main_column_button_addMidiChannel"; static constexpr auto MAIN_COLUMN_BUTTON_REMOVE = "main_column_button_remove"; static constexpr auto MAIN_CHANNEL_NOSAMPLE = "main_channel_noSample"; static constexpr auto MAIN_CHANNEL_SAMPLENOTFOUND = "main_channel_sampleNotFound"; static constexpr auto MAIN_CHANNEL_LABEL_PLAY = "main_channel_label_play"; static constexpr auto MAIN_CHANNEL_LABEL_ARM = "main_channel_label_arm"; static constexpr auto MAIN_CHANNEL_LABEL_STATUS = "main_channel_label_status"; static constexpr auto MAIN_CHANNEL_LABEL_READACTIONS = "main_channel_label_readActions"; static constexpr auto MAIN_CHANNEL_LABEL_MODEBOX = "main_channel_label_modeBox"; static constexpr auto MAIN_CHANNEL_LABEL_MUTE = "main_channel_label_mute"; static constexpr auto MAIN_CHANNEL_LABEL_SOLO = "main_channel_label_solo"; static constexpr auto MAIN_CHANNEL_LABEL_FX = "main_channel_label_fx"; static constexpr auto MAIN_CHANNEL_LABEL_VOLUME = "main_channel_label_volume"; static constexpr auto MAIN_CHANNEL_LABEL_MIDIACTIVITY = "main_channel_label_midiActivity"; static constexpr auto MAIN_CHANNEL_MENU_INPUTMONITOR = "main_channel_menu_inputMonitor"; static constexpr auto MAIN_CHANNEL_MENU_OVERDUBPROTECTION = "main_channel_menu_overdubProtection"; static constexpr auto MAIN_CHANNEL_MENU_LOADSAMPLE = "main_channel_menu_loadSample"; static constexpr auto MAIN_CHANNEL_MENU_EXPORTSAMPLE = "main_channel_menu_exportSample"; static constexpr auto MAIN_CHANNEL_MENU_KEYBOARDINPUT = "main_channel_menu_keyboardInput"; static constexpr auto MAIN_CHANNEL_MENU_MIDIINPUT = "main_channel_menu_midiInput"; static constexpr auto MAIN_CHANNEL_MENU_MIDIOUTPUT = "main_channel_menu_midiOutput"; static constexpr auto MAIN_CHANNEL_MENU_EDITSAMPLE = "main_channel_menu_editSample"; static constexpr auto MAIN_CHANNEL_MENU_EDITACTIONS = "main_channel_menu_editActions"; static constexpr auto MAIN_CHANNEL_MENU_CLEARACTIONS = "main_channel_menu_clearActions"; static constexpr auto MAIN_CHANNEL_MENU_CLEARACTIONS_ALL = "main_channel_menu_clearActions_all"; static constexpr auto MAIN_CHANNEL_MENU_CLEARACTIONS_VOLUME = "main_channel_menu_clearActions_volume"; static constexpr auto MAIN_CHANNEL_MENU_CLEARACTIONS_STARTSTOP = "main_channel_menu_clearActions_startStop"; static constexpr auto MAIN_CHANNEL_MENU_RENAME = "main_channel_menu_rename"; static constexpr auto MAIN_CHANNEL_MENU_CLONE = "main_channel_menu_clone"; static constexpr auto MAIN_CHANNEL_MENU_FREE = "main_channel_menu_free"; static constexpr auto MAIN_CHANNEL_MENU_DELETE = "main_channel_menu_delete"; static constexpr auto MISSINGASSETS_INTRO = "missingAssets_intro"; static constexpr auto MISSINGASSETS_AUDIOFILES = "missingAssets_audioFiles"; static constexpr auto MISSINGASSETS_PLUGINS = "missingAssets_plugIns"; static constexpr auto PLUGINCHOOSER_TITLE = "pluginChooser_title"; static constexpr auto PLUGINCHOOSER_HEADER = "pluginChooser_header"; static constexpr auto PLUGINCHOOSER_SORTBY = "pluginChooser_sortBy"; static constexpr auto PLUGINCHOOSER_SORTBY_NAME = "pluginChooser_sortBy_name"; static constexpr auto PLUGINCHOOSER_SORTBY_CATEGORY = "pluginChooser_sortBy_category"; static constexpr auto PLUGINCHOOSER_SORTBY_MANIFACTURER = "pluginChooser_sortBy_manifacturer"; static constexpr auto PLUGINCHOOSER_SORTBY_FORMAT = "pluginChooser_sortBy_format"; static constexpr auto PLUGINLIST_TITLE_MASTEROUT = "pluginList_title_masterOut"; static constexpr auto PLUGINLIST_TITLE_MASTERIN = "pluginList_title_masterIn"; static constexpr auto PLUGINLIST_TITLE_CHANNEL = "pluginList_title_channel"; static constexpr auto PLUGINLIST_ADDPLUGIN = "pluginList_addPlugin"; static constexpr auto PLUGINLIST_NOPROGRAMS = "pluginList_noPrograms"; static constexpr auto CHANNELNAME_TITLE = "channelName_title"; static constexpr auto KEYGRABBER_TITLE = "keyGrabber_title"; static constexpr auto KEYGRABBER_BODY = "keyGrabber_body"; static constexpr auto SAMPLEEDITOR_TITLE = "sampleEditor_title"; static constexpr auto SAMPLEEDITOR_RELOAD = "sampleEditor_reload"; static constexpr auto SAMPLEEDITOR_LOOP = "sampleEditor_loop"; static constexpr auto SAMPLEEDITOR_INFO = "sampleEditor_info"; static constexpr auto SAMPLEEDITOR_PAN = "sampleEditor_pan"; static constexpr auto SAMPLEEDITOR_PITCH = "sampleEditor_pitch"; static constexpr auto SAMPLEEDITOR_PITCH_TOBAR = "sampleEditor_pitch_toBar"; static constexpr auto SAMPLEEDITOR_PITCH_TOSONG = "sampleEditor_pitch_toSong"; static constexpr auto SAMPLEEDITOR_RANGE = "sampleEditor_range"; static constexpr auto SAMPLEEDITOR_SHIFT = "sampleEditor_shift"; static constexpr auto SAMPLEEDITOR_VOLUME = "sampleEditor_volume"; static constexpr auto SAMPLEEDITOR_TOOLS_CUT = "sampleEditor_tools_cut"; static constexpr auto SAMPLEEDITOR_TOOLS_COPY = "sampleEditor_tools_copy"; static constexpr auto SAMPLEEDITOR_TOOLS_PASTE = "sampleEditor_tools_paste"; static constexpr auto SAMPLEEDITOR_TOOLS_TRIM = "sampleEditor_tools_trim"; static constexpr auto SAMPLEEDITOR_TOOLS_SILENCE = "sampleEditor_tools_silence"; static constexpr auto SAMPLEEDITOR_TOOLS_REVERSE = "sampleEditor_tools_reverse"; static constexpr auto SAMPLEEDITOR_TOOLS_NORMALIZE = "sampleEditor_tools_normalize"; static constexpr auto SAMPLEEDITOR_TOOLS_FADE_IN = "sampleEditor_tools_fadeIn"; static constexpr auto SAMPLEEDITOR_TOOLS_FADE_OUT = "sampleEditor_tools_fadeOut"; static constexpr auto SAMPLEEDITOR_TOOLS_SMOOTH_EDGES = "sampleEditor_tools_smoothEdgdes"; static constexpr auto SAMPLEEDITOR_TOOLS_SET_BEGIN_END = "sampleEditor_tools_setBeginEnd"; static constexpr auto SAMPLEEDITOR_TOOLS_TO_NEW_CHANNEL = "sampleEditor_tools_toNewChannel"; static constexpr auto ACTIONEDITOR_TITLE = "actionEditor_title"; static constexpr auto ACTIONEDITOR_VOLUME = "actionEditor_volume"; static constexpr auto ACTIONEDITOR_KEYPRESS = "actionEditor_keyPress"; static constexpr auto ACTIONEDITOR_KEYRELEASE = "actionEditor_keyRelease"; static constexpr auto ACTIONEDITOR_STOPSAMPLE = "actionEditor_stopSample"; static constexpr auto ACTIONEDITOR_STARTSTOP = "actionEditor_startStop"; static constexpr auto ACTIONEDITOR_STARTSTOPDISABLED = "actionEditor_startStopDisabled"; static constexpr auto ACTIONEDITOR_VELOCITY = "actionEditor_velocity"; static constexpr auto ACTIONEDITOR_LABEL_ACTIONTYPE = "actionEditor_label_actionType"; static constexpr auto BROWSER_SHOWHIDDENFILES = "browser_showHiddenFiles"; static constexpr auto BROWSER_OPENPROJECT = "browser_openProject"; static constexpr auto BROWSER_SAVEPROJECT = "browser_saveProject"; static constexpr auto BROWSER_OPENSAMPLE = "browser_openSample"; static constexpr auto BROWSER_SAVESAMPLE = "browser_saveSample"; static constexpr auto BROWSER_OPENPLUGINSDIR = "browser_openPluginsDir"; static constexpr auto MIDIINPUT_MASTER_TITLE = "midiInput_master_title"; static constexpr auto MIDIINPUT_MASTER_ENABLE = "midiInput_master_enable"; static constexpr auto MIDIINPUT_MASTER_LEARN_REWIND = "midiInput_master_learn_rewind"; static constexpr auto MIDIINPUT_MASTER_LEARN_PLAYSTOP = "midiInput_master_learn_playStop"; static constexpr auto MIDIINPUT_MASTER_LEARN_ACTIONREC = "midiInput_master_learn_actionRec"; static constexpr auto MIDIINPUT_MASTER_LEARN_INPUTREC = "midiInput_master_learn_inputRec"; static constexpr auto MIDIINPUT_MASTER_LEARN_METRONOME = "midiInput_master_learn_metronome"; static constexpr auto MIDIINPUT_MASTER_LEARN_INVOLUME = "midiInput_master_learn_inVolume"; static constexpr auto MIDIINPUT_MASTER_LEARN_OUTVOLUME = "midiInput_master_learn_outVolume"; static constexpr auto MIDIINPUT_MASTER_LEARN_SEQDOUBLE = "midiInput_master_learn_seqDouble"; static constexpr auto MIDIINPUT_MASTER_LEARN_SEQHALF = "midiInput_master_learn_seqHalf"; static constexpr auto MIDIINPUT_CHANNEL_TITLE = "midiInput_channel_title"; static constexpr auto MIDIINPUT_CHANNEL_ENABLE = "midiInput_channel_enable"; static constexpr auto MIDIINPUT_CHANNEL_VELOCITYDRIVESVOL = "midiInput_channel_learn_velocityDrivesVol"; static constexpr auto MIDIINPUT_CHANNEL_LEARN_CHANNEL = "midiInput_channel_learn_channel"; static constexpr auto MIDIINPUT_CHANNEL_LEARN_KEYPRESS = "midiInput_channel_learn_keyPress"; static constexpr auto MIDIINPUT_CHANNEL_LEARN_KEYREL = "midiInput_channel_learn_keyRel"; static constexpr auto MIDIINPUT_CHANNEL_LEARN_KEYKILL = "midiInput_channel_learn_keyKill"; static constexpr auto MIDIINPUT_CHANNEL_LEARN_ARM = "midiInput_channel_learn_arm"; static constexpr auto MIDIINPUT_CHANNEL_LEARN_MUTE = "midiInput_channel_learn_mute"; static constexpr auto MIDIINPUT_CHANNEL_LEARN_SOLO = "midiInput_channel_learn_solo"; static constexpr auto MIDIINPUT_CHANNEL_LEARN_VOLUME = "midiInput_channel_learn_volume"; static constexpr auto MIDIINPUT_CHANNEL_LEARN_PITCH = "midiInput_channel_learn_pitch"; static constexpr auto MIDIINPUT_CHANNEL_LEARN_READACTIONS = "midiInput_channel_learn_readActions"; static constexpr auto MIDIOUTPUT_CHANNEL_TITLE = "midiOutput_channel_title"; static constexpr auto MIDIOUTPUT_CHANNEL_ENABLE = "midiOutput_channel_enable"; static constexpr auto MIDIOUTPUT_CHANNEL_ENABLE_LIGHTNING = "midiOutput_channel_enableLightning"; static constexpr auto MIDIOUTPUT_CHANNEL_LEARN_PLAYING = "midiOutput_channel_learn_playing"; static constexpr auto MIDIOUTPUT_CHANNEL_LEARN_MUTE = "midiOutput_channel_learn_mute"; static constexpr auto MIDIOUTPUT_CHANNEL_LEARN_SOLO = "midiOutput_channel_learn_solo"; static constexpr auto ABOUT_TITLE = "about_title"; static constexpr auto ABOUT_BODY = "about_body"; static constexpr auto ABOUT_BODY_VST = "about_body_vst"; static constexpr auto CONFIG_TITLE = "config_title"; static constexpr auto CONFIG_RESTARTGIADA = "config_restartGiada"; static constexpr auto CONFIG_AUDIO_TITLE = "config_audio_title"; static constexpr auto CONFIG_AUDIO_SYSTEM = "config_audio_system"; static constexpr auto CONFIG_AUDIO_BUFFERSIZE = "config_audio_bufferSize"; static constexpr auto CONFIG_AUDIO_SAMPLERATE = "config_audio_sampleRate"; static constexpr auto CONFIG_AUDIO_OUTPUTDEVICE = "config_audio_outputDevice"; static constexpr auto CONFIG_AUDIO_OUTPUTCHANNELS = "config_audio_outputChannels"; static constexpr auto CONFIG_AUDIO_LIMITOUTPUT = "config_audio_limitOutput"; static constexpr auto CONFIG_AUDIO_INPUTDEVICE = "config_audio_inputDevice"; static constexpr auto CONFIG_AUDIO_INPUTCHANNELS = "config_audio_inputChannels"; static constexpr auto CONFIG_AUDIO_RECTHRESHOLD = "config_audio_recThreshold"; static constexpr auto CONFIG_AUDIO_ENABLEINPUT = "config_audio_enableInput"; static constexpr auto CONFIG_AUDIO_RESAMPLING = "config_audio_reseampling"; static constexpr auto CONFIG_AUDIO_RESAMPLING_SINCBEST = "config_audio_reseampling_sincBest"; static constexpr auto CONFIG_AUDIO_RESAMPLING_SINCMEDIUM = "config_audio_reseampling_sincMedium"; static constexpr auto CONFIG_AUDIO_RESAMPLING_SINCBASIC = "config_audio_reseampling_sincBasic"; static constexpr auto CONFIG_AUDIO_RESAMPLING_ZEROORDER = "config_audio_reseampling_zeroOrder"; static constexpr auto CONFIG_AUDIO_RESAMPLING_LINEAR = "config_audio_reseampling_linear"; static constexpr auto CONFIG_AUDIO_NODEVICESFOUND = "config_audio_noDevicesFound"; static constexpr auto CONFIG_MIDI_TITLE = "config_midi_title"; static constexpr auto CONFIG_MIDI_SYSTEM = "config_midi_system"; static constexpr auto CONFIG_MIDI_OUTPUTPORT = "config_midi_outputPort"; static constexpr auto CONFIG_MIDI_INPUTPORT = "config_midi_inputPort"; static constexpr auto CONFIG_MIDI_NOPORTSFOUND = "config_midi_noPortsFound"; static constexpr auto CONFIG_MIDI_OUTPUTMIDIMAP = "config_midi_outputMidiMap"; static constexpr auto CONFIG_MIDI_NOMIDIMAPSFOUND = "config_midi_noMidiMapsFound"; static constexpr auto CONFIG_MIDI_SYNC = "config_midi_sync"; static constexpr auto CONFIG_MIDI_LABEL_ENABLEOUT = "config_midi_label_enableOut"; static constexpr auto CONFIG_MIDI_LABEL_ENABLEIN = "config_midi_label_enableIn"; static constexpr auto CONFIG_BEHAVIORS_TITLE = "config_behaviors_title"; static constexpr auto CONFIG_BEHAVIORS_CHANSSTOPONSEQHALT = "config_behaviors_chansStopOnSeqHalt"; static constexpr auto CONFIG_BEHAVIORS_TREATRECSASLOOPS = "config_behaviors_treatRecsAsLoops"; static constexpr auto CONFIG_BEHAVIORS_INPUTMONITORDEFAULTON = "config_behaviors_inputMonitorDefaultOn"; static constexpr auto CONFIG_BEHAVIORS_OVERDUBPROTECTIONDEFAULTON = "config_behaviors_overdubProtectionDefaultOn"; static constexpr auto CONFIG_BINDINGS_TITLE = "config_bindings_title"; static constexpr auto CONFIG_BINDINGS_PLAY = "config_bindings_play"; static constexpr auto CONFIG_BINDINGS_REWIND = "config_bindings_rewind"; static constexpr auto CONFIG_BINDINGS_RECORDACTIONS = "config_bindings_recordActions"; static constexpr auto CONFIG_BINDINGS_RECORDAUDIO = "config_bindings_recordAudio"; static constexpr auto CONFIG_BINDINGS_EXIT = "config_bindings_exit"; static constexpr auto CONFIG_MISC_TITLE = "config_misc_title"; static constexpr auto CONFIG_MISC_DEBUGMESSAGES = "config_misc_debugMessages"; static constexpr auto CONFIG_MISC_DEBUGMESSAGES_DISABLED = "config_misc_debugMessages_disabled"; static constexpr auto CONFIG_MISC_DEBUGMESSAGES_TOSTDOUT = "config_misc_debugMessages_toStdOut"; static constexpr auto CONFIG_MISC_DEBUGMESSAGES_TOFILE = "config_misc_debugMessages_toFile"; static constexpr auto CONFIG_MISC_TOOLTIPS = "config_misc_tooltips"; static constexpr auto CONFIG_MISC_TOOLTIPS_DISABLED = "config_misc_tooltips_disabled"; static constexpr auto CONFIG_MISC_TOOLTIPS_ENABLED = "config_misc_tooltips_enabled"; static constexpr auto CONFIG_MISC_LANGUAGE = "config_misc_language"; static constexpr auto CONFIG_MISC_NOLANGUAGESFOUND = "config_misc_noLanguagesFound"; static constexpr auto CONFIG_PLUGINS_TITLE = "config_plugins_title"; static constexpr auto CONFIG_PLUGINS_FOLDER = "config_plugins_folder"; static constexpr auto CONFIG_PLUGINS_SCANNING = "config_plugins_scanning"; static constexpr auto CONFIG_PLUGINS_SCAN = "config_plugins_scan"; static constexpr auto CONFIG_PLUGINS_INVALIDPATH = "config_plugins_invalidPath"; LangMap(); const char* get(const std::string&) const; private: Data m_data; std::string m_default; }; /* -------------------------------------------------------------------------- */ class LangMapper final : public Mapper { public: LangMapper(); /* get Gets a key from the currently loaded langmap file. */ const char* get(const std::string&) const; /* init Parses the langmap folders and find the available langmaps. */ void init(); /* read Reads a langmap from file 'file' and sets it as the current one. */ int read(const std::string& file); private: /* maps The current langmap selected and loaded. It might be the default one (En_US) if no langmaps have been found. */ LangMap m_map; }; } // namespace giada::v #endif giada-0.22.0/src/gui/types.h000066400000000000000000000024031425106661500155350ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_V_TYPES_H #define G_V_TYPES_H namespace giada::v { enum class Direction { HORIZONTAL, VERTICAL }; } // namespace giada::v #endifgiada-0.22.0/src/gui/ui.cpp000066400000000000000000000162021425106661500153430ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/ui.h" #include "core/const.h" #include "core/engine.h" #include "core/recorder.h" #include "gui/elems/mainWindow/keyboard/column.h" #include "gui/elems/mainWindow/keyboard/keyboard.h" #include "gui/elems/mainWindow/mainIO.h" #include "gui/elems/mainWindow/mainTimer.h" #include "gui/updater.h" #include "utils/gui.h" #include "utils/log.h" #ifdef WITH_VST #include #if defined(G_OS_LINUX) || defined(G_OS_FREEBSD) #include // For XInitThreads #endif #endif namespace giada::v { Ui::Ui(m::Recorder& recorder, const m::Conf::Data& conf) : dispatcher(conf.keyBindings) , m_updater(*this) , m_blinker(0) { dispatcher.onEventOccured = [&recorder]() { recorder.startActionRecOnCallback(); }; } /* -------------------------------------------------------------------------- */ bool Ui::shouldBlink() const { return m_blinker > BLINK_RATE / 2; } /* -------------------------------------------------------------------------- */ void Ui::load(const m::Patch::Data& patch) { reset(); mainWindow->keyboard->layout.clear(); for (const m::Patch::Column& col : patch.columns) mainWindow->keyboard->layout.push_back({col.id, col.width}); mainWindow->keyboard->rebuild(); setMainWindowTitle(patch.name); } /* -------------------------------------------------------------------------- */ void Ui::store(const std::string patchName, m::Patch::Data& patch) { patch.columns.clear(); mainWindow->keyboard->forEachColumn([&](const geColumn& c) { patch.columns.push_back({c.id, c.w()}); }); setMainWindowTitle(patchName); } /* -------------------------------------------------------------------------- */ void Ui::init(int argc, char** argv, m::Engine& engine) { /* 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 on our Juce-based PluginHost use Xlib concurrently. */ #if (defined(G_OS_LINUX) || defined(G_OS_FREEBSD)) && defined(WITH_VST) XInitThreads(); #endif langMapper.init(); langMapper.read(engine.conf.data.langMap); mainWindow = std::make_unique(G_MIN_GUI_WIDTH, G_MIN_GUI_HEIGHT, "", argc, argv, engine.conf.data); mainWindow->resize(engine.conf.data.mainWindowX, engine.conf.data.mainWindowY, engine.conf.data.mainWindowW, engine.conf.data.mainWindowH); setMainWindowTitle(engine.patch.data.name == "" ? G_DEFAULT_PATCH_NAME : engine.patch.data.name); m_updater.init(engine.model); if (engine.kernelAudio.isReady()) rebuildStaticWidgets(); } /* -------------------------------------------------------------------------- */ void Ui::reset() { setMainWindowTitle(G_DEFAULT_PATCH_NAME); rebuildStaticWidgets(); closeAllSubwindows(); mainWindow->clearKeyboard(); mainWindow->rebuild(); } /* -------------------------------------------------------------------------- */ void Ui::shutdown() { mainWindow.reset(); m_updater.close(); u::log::print("[ui] All windows closed\n"); } /* -------------------------------------------------------------------------- */ void Ui::refresh() { /* Update dynamic elements inside main window: in and out meters, beat meter and each channel. */ mainWindow->refresh(); /* Compute timer for blinker. */ m_blinker = (m_blinker + 1) % BLINK_RATE; /* Refresh Sample Editor and Action Editor for dynamic playhead. */ refreshSubWindow(WID_SAMPLE_EDITOR); refreshSubWindow(WID_ACTION_EDITOR); } /* -------------------------------------------------------------------------- */ void Ui::rebuild() { mainWindow->rebuild(); rebuildSubWindow(WID_FX_LIST); rebuildSubWindow(WID_SAMPLE_EDITOR); rebuildSubWindow(WID_ACTION_EDITOR); } /* -------------------------------------------------------------------------- */ void Ui::rebuildSubWindow(int wid) { v::gdWindow* w = getSubwindow(*mainWindow.get(), wid); if (w != nullptr) // If its open w->rebuild(); } /* -------------------------------------------------------------------------- */ void Ui::refreshSubWindow(int wid) { v::gdWindow* w = getSubwindow(*mainWindow.get(), wid); if (w != nullptr) // If its open w->refresh(); } /* -------------------------------------------------------------------------- */ v::gdWindow* Ui::getSubwindow(v::gdWindow& parent, int wid) { return parent.hasWindow(wid) ? parent.getChild(wid) : nullptr; } /* -------------------------------------------------------------------------- */ void Ui::openSubWindow(v::gdWindow& parent, v::gdWindow* child, int wid) { if (parent.hasWindow(wid)) { u::log::print("[GU] parent has subwindow with id=%d, deleting\n", wid); parent.delSubWindow(wid); } child->setId(wid); parent.addSubWindow(child); } /* -------------------------------------------------------------------------- */ void Ui::closeSubWindow(int wid) { mainWindow->delSubWindow(wid); } /* -------------------------------------------------------------------------- */ void Ui::closeAllSubwindows() { mainWindow->delSubWindow(WID_ACTION_EDITOR); mainWindow->delSubWindow(WID_SAMPLE_EDITOR); mainWindow->delSubWindow(WID_FX_LIST); mainWindow->delSubWindow(WID_FX); } /* -------------------------------------------------------------------------- */ void Ui::setMainWindowTitle(const std::string& s) { std::string out = std::string(G_APP_NAME) + " - " + s; mainWindow->copy_label(out.c_str()); } /* -------------------------------------------------------------------------- */ #ifdef WITH_VST void Ui::startJuceDispatchLoop() { Fl::add_timeout(G_GUI_REFRESH_RATE, juceDispatchLoop); } void Ui::stopJuceDispatchLoop() { Fl::remove_timeout(juceDispatchLoop); } #endif /* -------------------------------------------------------------------------- */ void Ui::rebuildStaticWidgets() { mainWindow->mainIO->rebuild(); mainWindow->mainTimer->rebuild(); } /* -------------------------------------------------------------------------- */ #ifdef WITH_VST void Ui::juceDispatchLoop(void*) { juce::MessageManager* mm = juce::MessageManager::getInstanceWithoutCreating(); assert(mm != nullptr); mm->runDispatchLoopUntil(1); Fl::add_timeout(G_GUI_REFRESH_RATE, juceDispatchLoop); } #endif } // namespace giada::v giada-0.22.0/src/gui/ui.h000066400000000000000000000074621425106661500150200ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_V_UI_H #define G_V_UI_H #include "core/conf.h" #include "core/patch.h" #include "gui/dialogs/mainWindow.h" #include "gui/dispatcher.h" #include "gui/langMapper.h" #include "gui/updater.h" #include #include namespace giada::m { class Recorder; class Engine; } // namespace giada::m namespace giada::v { class Ui final { public: Ui(m::Recorder&, const m::Conf::Data&); /* shouldBlink Return whether is time to blink something or not. This is used to make widgets blink. */ bool shouldBlink() const; /* load Reads UI information from a Patch when a new project has been loaded. */ void load(const m::Patch::Data&); /* store Writes UI information to a patch when a project needs to be saved. */ void store(const std::string patchName, m::Patch::Data& patch); void init(int argc, char** argv, m::Engine&); void reset(); void shutdown(); /* refresh Repaints dynamic GUI elements. */ void refresh(); /* rebuild Rebuilds the UI from scratch. Used when the model has changed. */ void rebuild(); /* [rebuild|refresh]SubWindow Rebuilds or refreshes subwindow with ID 'wid' if it exists, i.e. if it's open. */ void rebuildSubWindow(int wid); void refreshSubWindow(int wid); /* getSubwindow Returns a pointer to an open subwindow, otherwise nullptr. */ v::gdWindow* getSubwindow(v::gdWindow& parent, int wid); /* openSubWindow Opens a new sub-window as a child of parent and assigns 'wid' to child. */ void openSubWindow(v::gdWindow& parent, v::gdWindow* child, int wid); /* closeSubWindow Closes a sun-window currently attached to the main one. */ void closeSubWindow(int wid); /* closeAllSubwindows Closes all subwindows attached to the main one. */ void closeAllSubwindows(); /* setMainWindowTitle Updates the title of the main window, usually visible on top of it. */ void setMainWindowTitle(const std::string&); #ifdef WITH_VST /* [start|stop]JuceDispatchLoop Starts and stops the JUCE dispatch loop from its MessageManager component. This is needed for plugin-ins to wake up their UI editor and let it react to UI events. */ void startJuceDispatchLoop(); void stopJuceDispatchLoop(); #endif std::unique_ptr mainWindow; Dispatcher dispatcher; LangMapper langMapper; private: static constexpr int BLINK_RATE = G_GUI_FPS / 2; #ifdef WITH_VST static void juceDispatchLoop(void*); #endif /* rebuildStaticWidgets Updates attributes of static widgets, i.e. those elements that don't get automatically refreshed during the UI update loop. Useful when loading a new patch. */ void rebuildStaticWidgets(); Updater m_updater; int m_blinker; }; } // namespace giada::v #endifgiada-0.22.0/src/gui/updater.cpp000066400000000000000000000044041425106661500163730ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "gui/updater.h" #include "core/const.h" #include "core/model/model.h" #include "gui/ui.h" #include namespace giada::v { Updater::Updater(Ui& ui) : m_ui(ui) { } /* -------------------------------------------------------------------------- */ void Updater::init(m::model::Model& model) { model.onSwap = [this](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. */ u::gui::ScopedLock lock; type == m::model::SwapType::HARD ? m_ui.rebuild() : m_ui.refresh(); }; Fl::add_timeout(G_GUI_REFRESH_RATE, update, this); } /* -------------------------------------------------------------------------- */ void Updater::update(void* p) { static_cast(p)->update(); } /* -------------------------------------------------------------------------- */ void Updater::update() { m_ui.refresh(); Fl::add_timeout(G_GUI_REFRESH_RATE, update, this); } /* -------------------------------------------------------------------------- */ void Updater::close() { Fl::remove_timeout(update); } } // namespace giada::v giada-0.22.0/src/gui/updater.h000066400000000000000000000026661425106661500160500ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_V_UPDATER_H #define G_V_UPDATER_H namespace giada::m::model { class Model; } namespace giada::v { class Ui; class Updater final { public: Updater(Ui& ui); void init(m::model::Model&); void close(); private: static void update(void*); void update(); Ui& m_ui; }; } // namespace giada::v #endifgiada-0.22.0/src/main.cpp000066400000000000000000000026601425106661500150710ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "core/engine.h" #include "gui/ui.h" giada::m::Engine g_engine; giada::v::Ui g_ui(g_engine.recorder, g_engine.conf.data); int main(int argc, char** argv) { if (int ret = giada::m::init::tests(argc, argv); ret != -1) return ret; giada::m::init::startup(argc, argv); return giada::m::init::run(); }giada-0.22.0/src/mapper.cpp000066400000000000000000000041621425106661500154300ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "mapper.h" #include "utils/fs.h" #include #include namespace nl = nlohmann; namespace giada { void Mapper::init() { if (!std::filesystem::exists(m_mapsPath)) return; for (const auto& d : std::filesystem::directory_iterator(m_mapsPath)) { if (!d.is_regular_file()) // TODO - better mechanism to check if it's a valid map continue; m_mapFiles.push_back(d.path().filename().string()); } } /* -------------------------------------------------------------------------- */ std::optional Mapper::read(const std::string& file) const { if (file.empty()) return {}; std::ifstream ifs(u::fs::join(m_mapsPath, file)); if (!ifs.good()) return {}; nl::json j = nl::json::parse(ifs, nullptr, /*exceptions=*/false); if (j.is_discarded()) return {}; return {j}; } /* -------------------------------------------------------------------------- */ const std::vector& Mapper::getMapFilesFound() const { return m_mapFiles; } } // namespace giada giada-0.22.0/src/mapper.h000066400000000000000000000036651425106661500151040ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_MAPPER_H #define G_MAPPER_H #include #include #include #include namespace giada { class Mapper { public: /* getMapFilesFound Returns a reference to the list of maps found. */ const std::vector& getMapFilesFound() const; /* init Parses the map folder m_mapsPath and find the available maps. */ void init(); protected: /* read Reads a map from file 'file' as a JSON object. */ std::optional read(const std::string& file) const; /* m_mapsPath Path to folder containing map files, different between OSes. */ std::string m_mapsPath; /* m_mapFiles The available map files. Each element of the vector represents a map file found in the map folder. */ std::vector m_mapFiles; }; } // namespace giada #endif giada-0.22.0/src/utils/000077500000000000000000000000001425106661500145755ustar00rootroot00000000000000giada-0.22.0/src/utils/cocoa.h000066400000000000000000000030741425106661500160360ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * cocoa * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_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_getViewFromWindow(void* p); /* A bug on on OS X seems to misalign plugins' UI. The function takes care of fixing the positioning. TODO temporarily disabled: it does not work. */ //void cocoa_setWindowSize(void *p, int w, int h); #endif giada-0.22.0/src/utils/cocoa.mm000066400000000000000000000026721425106661500162230ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * cocoa * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2018 Giovanni A. Zuliani | Monocasual * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #import #import #import "cocoa.h" void* cocoa_getViewFromWindow(void* p) { NSWindow* win = (NSWindow* ) p; return (void*) win.contentView; } /* void cocoa_setWindowSize(void *p, int w, int h) { NSWindow *win = (NSWindow *) p; [win setContentSize:NSMakeSize(w, h)]; } */giada-0.22.0/src/utils/fs.cpp000066400000000000000000000126661425106661500157240ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * utils * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include #if defined(_WIN32) // getcwd (unix) or __getcwd (win) #include #include #else #include #endif #include #include #include #include #include #include #include #include // stat (fs::dirExists) #ifdef __APPLE__ #include // basename unix #include // getpwuid #endif #include "core/const.h" #include "utils/fs.h" #include "utils/log.h" #include "utils/string.h" namespace stdfs = std::filesystem; namespace giada::u::fs { bool fileExists(const std::string& s) { return stdfs::exists(s); } /* -------------------------------------------------------------------------- */ bool isDir(const std::string& s) { return stdfs::is_directory(s) && !isProject(s); } /* -------------------------------------------------------------------------- */ bool dirExists(const std::string& s) { return stdfs::exists(s); } /* -------------------------------------------------------------------------- */ bool mkdir(const std::string& s) { return dirExists(s) ? true : stdfs::create_directory(s); } /* -------------------------------------------------------------------------- */ std::string getRealPath(const std::string& s) { return s.empty() || !stdfs::exists(s) ? "" : stdfs::canonical(s).string(); } /* -------------------------------------------------------------------------- */ std::string basename(const std::string& s) { return stdfs::path(s).filename().string(); } /* -------------------------------------------------------------------------- */ std::string dirname(const std::string& s) { return stdfs::path(s).parent_path().string(); } /* -------------------------------------------------------------------------- */ std::string getCurrentPath() { return stdfs::current_path().string(); } /* -------------------------------------------------------------------------- */ std::string getExt(const std::string& s) { return stdfs::path(s).extension().string(); } /* -------------------------------------------------------------------------- */ std::string stripExt(const std::string& s) { 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", " "); return out; } /* -------------------------------------------------------------------------- */ std::string getHomePath() { #if defined(G_OS_LINUX) || defined(G_OS_FREEBSD) char buf[PATH_MAX]; snprintf(buf, PATH_MAX, "%s/.giada", getenv("HOME")); return stdfs::path(buf).string(); #elif defined(G_OS_WINDOWS) return stdfs::current_path().string(); #elif defined(G_OS_MAC) char buf[PATH_MAX]; struct passwd* pwd = getpwuid(getuid()); if (pwd == nullptr) { log::print("[getHomePath] unable to fetch user infos\n"); return ""; } const char* home = pwd->pw_dir; snprintf(buf, PATH_MAX, "%s/Library/Application Support/Giada", home); return stdfs::path(buf).string(); #endif } /* -------------------------------------------------------------------------- */ std::string getMidiMapsPath() { auto out = stdfs::path(getHomePath()) / "midimaps"; return out.string(); } std::string getLangMapsPath() { auto out = stdfs::path(getHomePath()) / "langmaps"; return out.string(); } /* -------------------------------------------------------------------------- */ bool isRootDir(const std::string& 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 (isRootDir(s)) return ""; #endif return stdfs::path(s).parent_path().string(); } /* -------------------------------------------------------------------------- */ std::string join(const std::string& a, const std::string& b) { auto out = stdfs::path(a) / stdfs::path(b); return out.string(); } } // namespace giada::u::fsgiada-0.22.0/src/utils/fs.h000066400000000000000000000050351425106661500153610ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * utils * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_UTILS_FS_H #define G_UTILS_FS_H #include namespace giada::u::fs { bool fileExists(const std::string& s); bool dirExists(const std::string& s); bool isDir(const std::string& s); /* isRootDir Tells whether 's' is '/' on Unix or '[X]:\' on Windows. */ bool isRootDir(const std::string& s); bool isProject(const std::string& s); bool mkdir(const std::string& s); std::string getCurrentPath(); std::string getHomePath(); std::string getMidiMapsPath(); std::string getLangMapsPath(); /* getRealPath Expands all symbolic links and resolves references to /./, /../ and extra / characters in the input path and returns the canonicalized absolute pathname. */ std::string getRealPath(const std::string& s); /* basename /path/to/file.txt -> file.txt */ std::string basename(const std::string& s); /* dirname /path/to/file.txt -> /path/to */ std::string dirname(const std::string& s); /* getExt /path/to/file.txt -> txt */ std::string getExt(const std::string& s); /* stripExt /path/to/file.txt -> /path/to/file */ std::string stripExt(const std::string& s); std::string stripFileUrl(const std::string& s); /* getUpDir Returns the upper directory: /path/to/my/directory -> /path/to/my/ */ std::string getUpDir(const std::string& s); /* join Joins two string paths using the correct separator. */ std::string join(const std::string& a, const std::string& b); } // namespace giada::u::fs #endif giada-0.22.0/src/utils/gui.cpp000066400000000000000000000140341425106661500160670ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include #include #include #include #if defined(_WIN32) #include "../ext/resource.h" #elif defined(__linux__) || defined(__FreeBSD__) #include #endif #include "core/conf.h" #include "core/graphics.h" #include "core/mixer.h" #include "core/mixerHandler.h" #include "core/plugins/pluginHost.h" #include "core/sequencer.h" #include "gui.h" #include "gui/dialogs/actionEditor/baseActionEditor.h" #include "gui/dialogs/sampleEditor.h" #include "gui/dialogs/warnings.h" #include "gui/dialogs/window.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/sequencer.h" #include "gui/elems/sampleEditor/waveTools.h" #include "log.h" #include "string.h" namespace giada::u::gui { ScopedLock::ScopedLock() { Fl::lock(); } /* -------------------------------------------------------------------------- */ ScopedLock::~ScopedLock() { Fl::unlock(); } /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ 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); #elif defined(_WIN32) w->icon((char*)LoadIcon(fl_display, MAKEINTRESOURCE(IDI_ICON1))); #endif } /* -------------------------------------------------------------------------- */ geompp::Rect getStringRect(const std::string& s) { int w = 0; int h = 0; fl_measure(s.c_str(), w, h); return {0, 0, w, h}; } /* -------------------------------------------------------------------------- */ geompp::Rect getCenterWinBounds(int w, int h) { return {centerWindowX(w), centerWindowY(h), w, h}; } /* -------------------------------------------------------------------------- */ 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, "_", "-"); return out; } /* -------------------------------------------------------------------------- */ std::string truncate(const std::string& s, Pixel width) { if (s.empty() || getStringRect(s).w <= width) return s; std::string tmp = s; std::size_t size = tmp.size(); while (getStringRect(tmp + "...").w > width) { if (size == 0) return ""; tmp.resize(--size); } return tmp + "..."; } /* -------------------------------------------------------------------------- */ int centerWindowX(int w) { return (Fl::w() / 2) - (w / 2); } int centerWindowY(int h) { return (Fl::h() / 2) - (h / 2); } /* -------------------------------------------------------------------------- */ std::string keyToString(int key) { // https://github.com/fltk/fltk/blob/570a05a33c9dc42a16caa5a1a11cf34d4df1c1f9/FL/Enumerations.H // https://www.fltk.org/doc-1.3/group__fl__events.html#gafa17a5b4d8d9163631c88142e60447ed if (key == 0) return "[None]"; switch (key) { case ' ': return "Space"; case FL_BackSpace: return "Backspace"; case FL_Tab: return "Tab"; case FL_Enter: return "Enter"; case FL_Pause: return "Pause"; case FL_Scroll_Lock: return "Scroll lock"; case FL_Escape: return "Escape"; case FL_Home: return "Home"; case FL_Left: return "Left"; case FL_Up: return "Up"; case FL_Right: return "Right"; case FL_Down: return "Down"; case FL_Page_Up: return "Page up"; case FL_Page_Down: return "Page down"; case FL_End: return "End"; case FL_Print: return "Print"; case FL_Insert: return "Insert"; case FL_Menu: return "Menu"; case FL_Help: return "Help"; case FL_Num_Lock: return "Num lock"; case FL_KP: // TODO ? return ""; case FL_KP_Enter: return "KP Enter"; case FL_F + 1: return "F1"; case FL_F + 2: return "F2"; case FL_F + 3: return "F3"; case FL_F + 4: return "F4"; case FL_F + 5: return "F5"; case FL_F + 6: return "F6"; case FL_F + 7: return "F7"; case FL_F + 8: return "F8"; case FL_F + 9: return "F9"; case FL_F + 10: return "F10"; case FL_F + 11: return "F11"; case FL_F + 12: return "F12"; case FL_Shift_L: return "Shift L"; case FL_Shift_R: return "Shift R"; case FL_Control_L: return "Control L"; case FL_Control_R: return "Control R"; case FL_Caps_Lock: return "Caps lock"; case FL_Meta_L: return "Meta L"; case FL_Meta_R: return "Meta R"; case FL_Alt_L: return "Alt L"; case FL_Alt_R: return "Alt R"; case FL_Delete: return "Delete"; default: return Fl::event_text(); } } } // namespace giada::u::gui giada-0.22.0/src/utils/gui.h000066400000000000000000000050641425106661500155370ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_UTILS_GUI_H #define G_UTILS_GUI_H #include "core/types.h" #include "deps/geompp/src/rect.hpp" #include #include namespace giada::v { class gdWindow; } namespace giada::u::gui { /* ScopedLock ScopedLock for locking the main FLTK thread when the UI must be updated from a secondary thread. */ class ScopedLock { public: ScopedLock(); ~ScopedLock(); }; void setFavicon(v::gdWindow* w); /* removeFltkChars Strips special chars used by FLTK to split menus into sub-menus. */ std::string removeFltkChars(const std::string& s); /* getStringRect Returns the bounding box in pixels of a string 's'. */ geompp::Rect getStringRect(const std::string& s); /* getCenterWinBounds Returns the bounding box to be used for a centered window. */ geompp::Rect getCenterWinBounds(int w, int h); /* truncate Adds ellipsis to a string 's' if it longer than 'width' pixels. */ std::string truncate(const std::string& s, Pixel width); int centerWindowX(int w); int centerWindowY(int h); /* keyToString Translates an FLTK key event into a human-readable string. */ std::string keyToString(int key); /* makeMenuItem Makes a new Fl_Menu_Item at compile time. Used to initialize pop-up menus. */ constexpr Fl_Menu_Item makeMenuItem(const char* text, Fl_Callback* callback = nullptr, void* data = nullptr, int flags = 0) { return Fl_Menu_Item{text, 0, callback, data, flags, 0, 0, 0, 0}; } } // namespace giada::u::gui #endif giada-0.22.0/src/utils/log.cpp000066400000000000000000000032011425106661500160560ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * log * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "log.h" #include #include namespace giada::u::log { int init(int m) { mode = m; stat = true; if (mode == LOG_MODE_FILE) { std::string fpath = u::fs::join(fs::getHomePath(), "giada.log"); 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); } } // namespace giada::u::log giada-0.22.0/src/utils/log.h000066400000000000000000000054151425106661500155340ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * log * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_UTILS_LOG_H #define G_UTILS_LOG_H #include "core/const.h" #include "utils/fs.h" #include #include #include #include namespace giada::u::log { inline FILE* f; inline int mode; inline bool stat; /* init Initializes logger. Mode defines where to write the output: LOG_MODE_STDOUT, LOG_MODE_FILE and LOG_MODE_MUTE. */ int init(int mode); void close(); /* string_to_c_str Internal utility function for string transformation. Uses forwarding references (&&) to avoid useless string copy. */ 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::string>) // If the argument is a std::string return an old-style C-string return s.c_str(); else // Return the argument unchanged otherwise return s; }; /* print A variadic printf-like logging function. Any `std::string` argument will be automatically transformed into a C-string. */ template static void print(const char* format, Args&&... args) { if (mode == LOG_MODE_MUTE) return; 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))...); #ifdef _WIN32 fflush(f); #endif } else std::printf(format, string_to_c_str(std::forward(args))...); } } // namespace giada::u::log #endif giada-0.22.0/src/utils/math.cpp000066400000000000000000000033321425106661500162330ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "math.h" #include 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); } /* -------------------------------------------------------------------------- */ float dBtoLinear(float f) { return std::pow(10, f / 20.0f); } } // namespace math } // namespace u } // namespace giadagiada-0.22.0/src/utils/math.h000066400000000000000000000040371425106661500157030ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_UTILS_MATH_H #define G_UTILS_MATH_H #include #include namespace giada::u::math { float linearToDB(float f); float dBtoLinear(float f); int quantize(int x, int step); /* -------------------------------------------------------------------------- */ /* map (1) Maps 'x' in range [a, b] to a new range [w, z]. Source: https://en.wikipedia.org/wiki/Linear_equation#Two-point_form*/ template TO map(TI x, TI a, TI b, TO w, TO z) { static_assert(std::is_arithmetic_v); static_assert(std::is_arithmetic_v); if (a == b) // Prevents division by zero (undefined behavior) return x; return (((x - a) / (double)(b - a)) * (z - w)) + w; } /* map (2) Maps 'x' in range [0, b) to a new range [0, z]. */ template TO map(TI x, TI b, TO z) { return map(x, static_cast(0), b, static_cast(0), z); } } // namespace giada::u::math #endif giada-0.22.0/src/utils/string.cpp000066400000000000000000000065331425106661500166160ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * utils * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "string.h" #include "core/const.h" #include #include #include #include #include namespace giada { namespace u { namespace string { /* TODO - use std::to_string() */ std::string fToString(float f, int precision) { std::stringstream out; out << std::fixed << std::setprecision(precision) << f; 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); } /* -------------------------------------------------------------------------- */ 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) { in.replace(pos, search.length(), replace); pos += replace.length(); } return in; } /* -------------------------------------------------------------------------- */ std::string format(const char* format, ...) { va_list args; /* Compute the size of the new expanded std::string (i.e. with replacement taken into account). */ 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 tmp(new char[size]); /* Fill the temporary std::string with the formatted data. */ va_start(args, format); vsprintf(tmp.get(), format, args); va_end(args); return std::string(tmp.get(), tmp.get() + size - 1); } /* -------------------------------------------------------------------------- */ std::vector split(std::string in, std::string sep) { std::vector out; 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); return out; } } // namespace string } // namespace u } // namespace giada giada-0.22.0/src/utils/string.h000066400000000000000000000035061425106661500162600ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * utils * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_UTILS_STRING_H #define G_UTILS_STRING_H #include #include #include namespace giada { namespace u { namespace string { template 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(); } std::string replace(std::string in, const std::string& search, const std::string& replace); std::string trim(const std::string& s); std::vector split(std::string in, std::string sep); std::string fToString(float f, int precision); std::string format(const char* format, ...); } // namespace string } // namespace u } // namespace giada #endif giada-0.22.0/src/utils/time.cpp000066400000000000000000000026051425106661500162420ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * utils * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "time.h" #include #include namespace giada { namespace u { namespace time { void sleep(int millisecs) { std::this_thread::sleep_for(std::chrono::milliseconds(millisecs)); } } // namespace time } // namespace u } // namespace giada giada-0.22.0/src/utils/time.h000066400000000000000000000024531425106661500157100ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * utils * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_UTILS_TIME_H #define G_UTILS_TIME_H namespace giada { namespace u { namespace time { void sleep(int millisecs); } } // namespace u } // namespace giada #endifgiada-0.22.0/src/utils/vector.h000066400000000000000000000052011425106661500162460ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_UTILS_VECTOR_H #define G_UTILS_VECTOR_H #include #include #include #include namespace giada::u::vector { template std::size_t indexOf(const T& v, const P& p) { return std::distance(std::cbegin(v), std::find(std::cbegin(v), std::cend(v), p)); } /* -------------------------------------------------------------------------- */ template auto findIf(const T& v, F&& func) { return std::find_if(std::cbegin(v), std::cend(v), func); } /* -------------------------------------------------------------------------- */ template bool has(const T& v, F&& func) { return findIf(v, func) != std::cend(v); } /* -------------------------------------------------------------------------- */ template void removeIf(T& v, F&& func) { v.erase(std::remove_if(v.begin(), v.end(), func), v.end()); } template void remove(T& v, const V& o) { v.erase(std::remove(v.begin(), v.end(), o), v.end()); } /* -------------------------------------------------------------------------- */ template std::vector cast(const I& i) { return {i.begin(), i.end()}; } /* -------------------------------------------------------------------------- */ template auto atOr(const Vector& v, int index, Default d) { return index >= 0 && static_cast(index) < v.size() ? v[index] : d; } } // namespace giada::u::vector #endif giada-0.22.0/src/utils/ver.cpp000066400000000000000000000035431425106661500161020ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #include "ver.h" #include "core/const.h" #include "deps/rtaudio/RtAudio.h" #include #include namespace giada { namespace u { namespace ver { std::string getLibsndfileVersion() { char buffer[128]; sf_command(nullptr, SFC_GET_LIB_VERSION, buffer, sizeof(buffer)); return std::string(buffer); } /* -------------------------------------------------------------------------- */ std::string getRtAudioVersion() { #ifdef TESTS return ""; #else return RtAudio::getVersion(); #endif } /* -------------------------------------------------------------------------- */ std::string getRtMidiVersion() { #ifdef TESTS return ""; #else return RtMidi::getVersion(); #endif } } // namespace ver } // namespace u } // namespace giada giada-0.22.0/src/utils/ver.h000066400000000000000000000026131425106661500155440ustar00rootroot00000000000000/* ----------------------------------------------------------------------------- * * Giada - Your Hardcore Loopmachine * * ----------------------------------------------------------------------------- * * Copyright (C) 2010-2022 Giovanni A. Zuliani | Monocasual Laboratories * * This file is part of Giada - Your Hardcore Loopmachine. * * Giada - Your Hardcore Loopmachine is free software: you can * redistribute it and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * Giada - Your Hardcore Loopmachine is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Giada - Your Hardcore Loopmachine. If not, see * . * * -------------------------------------------------------------------------- */ #ifndef G_UTILS_VER_H #define G_UTILS_VER_H #include namespace giada { namespace u { namespace ver { std::string getLibsndfileVersion(); std::string getRtAudioVersion(); std::string getRtMidiVersion(); } // namespace ver } // namespace u } // namespace giada #endif giada-0.22.0/tests/000077500000000000000000000000001425106661500140105ustar00rootroot00000000000000giada-0.22.0/tests/actionRecorder.cpp000066400000000000000000000032761425106661500174670ustar00rootroot00000000000000#include "src/core/actions/actionRecorder.h" #include "src/core/actions/action.h" #include "src/core/actions/actions.h" #include "src/core/const.h" #include "src/core/model/model.h" #include "src/core/types.h" #include TEST_CASE("ActionRecorder") { using namespace giada; using namespace giada::m; model::Model model; ActionRecorder ar(model); REQUIRE(ar.hasActions(/*ch=*/0) == false); SECTION("Test record") { const int ch = 0; const Frame f1 = 10; const Frame f2 = 70; const MidiEvent e1 = MidiEvent(MidiEvent::NOTE_ON, 0x00, 0x00); const MidiEvent e2 = MidiEvent(MidiEvent::NOTE_OFF, 0x00, 0x00); const Action a1 = ar.rec(ch, f1, e1); const Action a2 = ar.rec(ch, f2, e2); REQUIRE(ar.hasActions(ch) == true); REQUIRE(a1.frame == f1); REQUIRE(a2.frame == f2); REQUIRE(a1.prevId == 0); REQUIRE(a1.nextId == 0); REQUIRE(a2.prevId == 0); REQUIRE(a2.nextId == 0); SECTION("Test clear actions by channel") { const int ch = 1; const Frame f1 = 100; const Frame f2 = 200; const MidiEvent e1 = MidiEvent(MidiEvent::NOTE_ON, 0x00, 0x00); const MidiEvent e2 = MidiEvent(MidiEvent::NOTE_OFF, 0x00, 0x00); ar.rec(ch, f1, e1); ar.rec(ch, f2, e2); ar.clearChannel(/*channel=*/0); REQUIRE(ar.hasActions(/*channel=*/0) == false); REQUIRE(ar.hasActions(/*channel=*/1) == true); } SECTION("Test clear actions by type") { ar.clearActions(/*channel=*/0, MidiEvent::NOTE_ON); ar.clearActions(/*channel=*/0, MidiEvent::NOTE_OFF); REQUIRE(ar.hasActions(/*channel=*/0) == false); } SECTION("Test clear all") { ar.clearAllActions(); REQUIRE(ar.hasActions(/*channel=*/0) == false); } } } giada-0.22.0/tests/main.cpp000066400000000000000000000001271425106661500154400ustar00rootroot00000000000000#define CATCH_CONFIG_MAIN #define CATCH_CONFIG_FAST_COMPILE #include giada-0.22.0/tests/midiLighter.cpp000066400000000000000000000045511425106661500167620ustar00rootroot00000000000000#include "../src/core/channels/midiLighter.h" #include "mocks/kernelMidiMock.h" #include #include TEST_CASE("MidiMapper") { using namespace giada; m::KernelMidiMock kernelMidi; m::MidiMapper midiMapper(kernelMidi); m::MidiLighter midiLighter(midiMapper); midiMapper.currentMap = { "test-brand", "test-device", {{0, "0x000000", 0, 0x000000}}, // init commands {0, "0x000001", 0, 0x000001}, // mute on {0, "0x000002", 0, 0x000002}, // mute off {0, "0x000003", 0, 0x000003}, // solo on {0, "0x000004", 0, 0x000004}, // solo off {0, "0x000005", 0, 0x000005}, // waiting {0, "0x000006", 0, 0x000006}, // playing {0, "0x000007", 0, 0x000007}, // stopping {0, "0x000008", 0, 0x000008}, // stopped {0, "0x000009", 0, 0x000009}, // playingInaudible }; midiLighter.onSend = []() {}; midiLighter.playing = {0x000010, 0}; midiLighter.mute = {0x000011, 0}; midiLighter.solo = {0x000012, 0}; SECTION("Test initialization") { REQUIRE(midiLighter.enabled == false); } SECTION("Test send OFF status") { midiLighter.sendStatus(ChannelStatus::OFF, /*audible=*/true); REQUIRE(kernelMidi.sent.back() == 0x000008); // Stopped } SECTION("Test send WAIT status") { midiLighter.sendStatus(ChannelStatus::WAIT, /*audible=*/true); REQUIRE(kernelMidi.sent.back() == 0x000005); // Waiting } SECTION("Test send ENDING status") { midiLighter.sendStatus(ChannelStatus::ENDING, /*audible=*/true); REQUIRE(kernelMidi.sent.back() == 0x000007); // Stopping } SECTION("Test send PLAY status") { midiLighter.sendStatus(ChannelStatus::PLAY, /*audible=*/true); REQUIRE(kernelMidi.sent.back() == 0x000006); // Playing midiLighter.sendStatus(ChannelStatus::PLAY, /*audible=*/false); REQUIRE(kernelMidi.sent.back() == 0x000009); // Playing inaudible } SECTION("Test send mute") { midiLighter.sendMute(/*isMuted=*/true); REQUIRE(kernelMidi.sent.back() == 0x000001); // Mute on midiLighter.sendMute(/*isMuted=*/false); REQUIRE(kernelMidi.sent.back() == 0x000002); // Mute off } SECTION("Test send solo") { midiLighter.sendSolo(/*isSoloed=*/true); REQUIRE(kernelMidi.sent.back() == 0x000003); // Solo on midiLighter.sendSolo(/*isSoloed=*/false); REQUIRE(kernelMidi.sent.back() == 0x000004); // Solo off } } giada-0.22.0/tests/mocks/000077500000000000000000000000001425106661500151245ustar00rootroot00000000000000giada-0.22.0/tests/mocks/kernelMidiMock.h000066400000000000000000000003531425106661500201730ustar00rootroot00000000000000#ifndef G_TESTS_KERNELMIDI_MOCK_H #define G_TESTS_KERNELMIDI_MOCK_H namespace giada::m { class KernelMidiMock { public: void send(uint32_t s) { sent.push_back(s); } std::vector sent; }; } // namespace giada::m #endifgiada-0.22.0/tests/resources/000077500000000000000000000000001425106661500160225ustar00rootroot00000000000000giada-0.22.0/tests/resources/test.wav000066400000000000000000002366441425106661500175370ustar00rootroot00000000000000RIFF=WAVEfmt DXdatax='hG6z z&2Tt j 7mV?[ob`B<1+EWld .+;Y,SVWw?,2 ^a .>9J\0_(t G{>Q6j4$(F=%Ga9;}sVe{~Y,_![!^6\45[3[Kr`62 /$('f&F%NlTh}3 xbW^[:eYVx7[Q&x~b/(b]81](n9 nE 8-CGU- r>h:%PCmo61P&X:Kl)&$up#,F#q(lK&I.$qo*PVAM9}z^9}-Q<|{SlV -St? lKv}.h'3k@yK|#ZB1iOK |AUP /jg#|Z2mm s`FY{IaOwcEz+;,c}`=!Pk s~pC7We[JT[(sN&,g( <(U!&@ &>b0DR[4.ARi?F?F@9 ne>B"zi4\^[%Vj!B?V<ZIE1T:9(W?vEP 1q/sE\ O~@xL9 7nU ;%;1[n RVGG4Ib%32iwS ]^" ^8\g8 mBy"Y\hu<~7kJ'^\FSTxghN ^+ 4h-  E&Fh|c   2J ' H:(Me@w c d +h0 7JuSg wp,{Z5w jHD W ,e c." {)h 'o5(}UsnNm . : C>n*}Ax:?8s]` %U(7Nj %quDS:dyP_ l six%G-d_~(k?Oat V]O2 ]we/2H+)\ w$eU ^a!'_!y1/ow{ZxY ZLqhF JV ^q |$Ps,J5, $oF[H! 0+]ng ey}k}8`I 0 ^Mxn>zt1E C t3!+ 7wq_+hn$5mB2k"I%| l&2c .p8oI5G  E]\ Y = zCt 2OP - C K ,,"gk \Zn # 4 s~WJ^S1 zqBc )d-Yl; d? Ev'TS &&V?_j`8mP^<\@_ VUW \]ZM.MJj%Pv zbh)v7-B~q]O wg#L D| {3: R" 'x \7JS  w >~&A: V$Sys6 i :xlrO+f&*8oR x jF~# Y!m,iv}q/mismr PFZ K\Fe`HA[; `*O e Oc* 410k1J\Hf%RDY11b,T!&~0< mK35bI7jsAhccEi(hdrF G(akvb_r1<^4'l xBUC*a+ i/ jk]K+nIN&X $x^'W<&];!0:HE;<7X7 o<m T6h6arc4=_f%qeOI>4 DGhb a6N7i}'n_x:[~-\(&"Q1(d2w]bE]t~feL%B tBi.M<PI Cq('BsA~9G3+m ^ yO&+Y2b'MZlCl~oq5U  'x Y>(H2wZQ#wso<e]O N|Xb${W #_GbXl%UUz 7)Itg#7_{6kv+/KdTeu~*D_7D8z +Xmwzx f(^PIH|;Eno~. "B:+i[M*(Dz.{"8Z&cB'h44*5 :my:!zc"xy^d*/['(A$z :QPj.hWRN9Un>~5dQe@/lsh[3M k gDqoqC]HC J7,]@IydKl] :uNE  FVd(pZLj8g|:GR^f"67{ YRH},T48de.,+],6$u}G1;vFP;*LT)5J40 &nnFdEsn]&Wz- N{t3ApAeI)|f1A_>i+7oeHa?|4{]4oPU];77}E~}_?gdNvXzBZ-}c LC94&w?#WEFv1$KHEB j z}h@(8>#L]bMtVdTz yi3L+`?6m}9@+D%AQv)Rn bc; i|: 1D*AxkPKuCt>ZBD3*/b3XiEk1cOGb61YqQRmJ/\uCV* _[HQjWo#VdDlK=+=a 3.h'>&pZ&y0%mN tCv dxg 'o9? lDH: R1R($X )8 6}1M[/-*QbWH  w1< kgvWlxE\a?{>**{<!6- ]s3~07@KU ACoD 44n1SDg@m!GbFlPIgl>9b8lJ Dl>Px0% LKT S 6>L `f78ya? {q/n"~X\6h5;dsp>eBKG&:HN_yS+- hO%hsR^Q`ABRK84|FE cfaTbRe)0 z 25 '- Q ( :Xm O  7ktT:HsT,A7Zgp~%YH_y XDMb EX%V*5gl1 d0wjyDUMgngm64BaP/ byIRob:F(=[2^!R]z dQmOZ"" P@*-l&x:V6[vf%j -W J~TY^ qc\A0Fyrmy";g!yQ}I7 J@w: dj{NN 'm)KSn>V6:p=)!ztMY BLqW=j7.m,>$A9w(5p,K6NfS>fcPhYUL s| *%p44T@Ou<hp8 #`/^e/{m*&5Tn'>lT8q.?{V~RX:j}Z#u }Y=m ra?L3ICtb(rasBZ#MR> dGT+pYI H; 7$d K+ 7:$ =w~4$uX!Yx.kBg|n2x]B@%9z#WY}Ucu+Ys*Osq}+L%[ '? 2N>>aG _oS \k~oaVB*m=\IEY` _w09Ov'3`t h/`zmu#)>;1Fq ""{|F!F. `Z; Ws,E\#P $"s kN(&Mr?xJ,C 7z/ i 'Q x|^FZ@ho"zQ($Mfi*SS^V| . %!/ <xi UogolAfZl+tSaL0<j(Zm<hBa M{% Z}1>oS0Z5 dj6%C~GE;jp G^n,PfL:B B*p?~RL5@1/W$MDz>&z+Gx!WR\ 0Jg /h"*VC}\89@6'f($O*ZJ_1uED9p\2'H}uZ Nff p>7VkU#5f4zRM xp6VJ_`(Bj)~&rSv,M[|v<Sa`eID*r[GvZk>`?h1_EXJed5%B^-jZ:mEB^T[T,.cQ0-w:,5q!gD=kM?hqsIn/OMkRB[jIdQe46K(r|01Z!9h` r)y(" EGwT4&:6oDJo8!(_?ap<Tbbaff.Af)nTmK -9#o |1r:* *AGvfd4W _fb(DdE)$ t+Ri\&0'\cj.{BgwY)Lv&=xyMc'g&HJ'%:Ep!ES E[vUL_$dYa 56/8o(fFM <A(><iHza$'_y"fr2>R )e44x=N }k6N| g0fKiC]A8l_; /%7\P1%XWMTOF"&Kuh ougok1Xn /Cu\t#2?yTSbCol: P\;L0>T-Jh#B R&Olos8x:t7ThUFx T)bfjztpzs=Q_l3HefRtz:zSty NI%;~MkOwsd;v_IqrYrS0^CjYWO%9nv/c0o9M~p[;<C \0 O/=YwZ'zZwY6hlXZQ+stImOqoF&ka Qw;s] FAI,M?ycHSDm jP?*@ym 3= gM-}o!eC[? Fe`%YlZ5@J7peEQe?jv+6">eC^WJ?*J !1g$i?w< )@Fs({`4vRv/0 $`b`3HOy-DG_$^5<nyB0IEswcncMm@`^U,X>J?y[K40y,~#MnT(ajX~!>z%rI%}Na5'Dn<]lb|;&}iF@BJDWUEysiT o -- UqR Ji3Q_x6TSpq9+&a}YUJh_\ mb|'\I?#H$k_n@*?HW|.>ZQNFCtLO/n IF2^q3)G:=;E4WE B@>o,\@v66Pj54,.gw][d13LWe5Y5Uk")^>HV"omD=N06tLG8|hv?-.yxMKgggih Z.59**j#c_UC1*\o@%LY_[a9%RPeeZ348{mcOP@Q15T;+!LSMf jI)|?'V ^cYQIad`t_+dL M~BY'\I\<vv^b"}I%EUfRz#ktq@F| vjR |"aKM{?OJ{/>/Dwez>m\q^AULl^L8_QTY<D3c(7[tMR~zmX[]RxMMug_ gHchyuh_=9>zo a~QVm <.ZM$dIY[xvI2 v[IIf 3>Dv}jG#D*j2iI:AAlnj88gtUZ.lX"4+bS&\\N`Jk u|s6(WQ!Yu]Sjp2i9OCE1thlNbB* [yE)OWfXG62 M+jDD zV, 9X36Y9_Lq "sS{m%sPTqn] ^}8<;q}q^<2+:793d| V?9De|9!S,d;Lz)[g{P;>8u[1&j_N/K6d`bv+h8 "Z'Q'mr{$ \39,uc"K9AX-T;vf;av|b %1cexKK_sy6q%$;_T w&+ {>&C(KvzAW+[K(a&t9L~N5{ydIB< xVedZ/82c{#U5"rds$ed4)G.L%,qS w/4M{;-ib'bUpCppUjH/G9"N'N@q.4* ys'*cq4G&I WcO[R6\pIid2"7N?|^A X3SP7'p4q<inTm=oR(Pb=R|uP Bef CA_^+BF+8qX|G,5i}j*Z-~*Z(b}_tBgnUPznBiZ Ob 4E>VL[tt\*c`]:s]6U LYEC=&=7I0= qs]'~[';(Km~jc!HbjpS0 E!6zyb/NEVOnQ}'~w!8:ViFudb.L/ ?+bo]<~AH]%@M`){5yW/0#_).[ _g;Z16GGT 3||*uk}j i3gI 1 G e)^*-0zRW% )>} D_%/m.W@Wx+d]Wj(1` 'q\sUfBc*/Ew6u`5_ZA:y+i H{ K6:% gz4)uv~]tnZ/91lgx T(O2!@`zYr 31QUfx  G }:Em, :/yLSKi^Rnk Z,=S-?}X.G 8qm3wG1`})<>%>"[OS\3^'v9]M_{T:p >SXzS3^|p5u$!q=k5Z- -:lIu^HJ9M#q]&x_bddO/B(EhzN;U1Jm|&T;YQ4S7m< Nr(;1D7%KIH 8kMuhL/L;lrD=P@]9Qy\en'UWFyf`t$`|__Ct*xV_& k4_q8y7)ZyZ Y28p;nxTWuERCF\k0K AO4 S|Sm[^4?MmLI3f~sM|ivJ6DU-(:Cc< ;Ua6lKg9<JMb|4gluVWdVXcH 8u1 3G} M/W.~cAqu|44 I,Mt.Q-CzGMA=fGguua)\O?.%q2HnkQz3s=CK6rf[; ]D^Iw,R_t^j}u:q T*d~%f;v{t;Zw,wTHeDW%O#s/9&!Up +S]uQ^$1a5o?Hz)rRhu]g;VP_pO_pYSyS)6tAbxH]qZZ 8J% B|9 dl' r7 H^&;7lLG2m4R686=p*o_YVzLYChmFFvx;~6\.#{GL7"!K/RtDj=q( gaI$=T_i !qJ b"0Fp.-~O($2N} Ok(>qbAmTwgD!apr;Ual -cj6.va]J?x  SeKb{J03)P#_GKB}UpsyuqdC?r >b]"|#ih8aHG,8GEr a!P8=y"} sh\qf|dATX,4+qlSn6]OUU-6:@[(uvgz}<"Fz">P"ymq{yc|n~ fJV#|$XDEQ$@kbq]GwxfgQ\^bxj*)j=+JC<$IM,/z<O!rA>gyP 7^4E,9J|VBU$.)ERyjQ/AXNJ"{(%6iP>)9j{(nx@WAy*-j3 im D5myU1]9cI"$E JO4RC x]nNB0 Cch6]P%LZ;==&$bo]t0B])5{, o1,+b]'Y104zt-VGj'x)ooX_!aXR:ksH"[E*Y[<[#O2AA+ 1o2/{ 7eMBQj?pB{BqAl*LL`;$ <+y8_~$Y A>BL}X~O,:our[y@tTNj7*'1/! %R8nqX~E]0a'wI !GN$;.pc{3b!5jq!]D6VQuB(y5X;Y_@q330"11x LhsX]:vj7@g`SKa-B 9R2x .$WpW8`Y*Cdrl)|Yk*h R6vK zR\, 92wNx3pI^K|%\Y9lS}V&5v+WQS1KRyxqxU'TA@OWYj:VG:R)PqaP0ow7B|q)$3:Yoy *umB=^>OdNSdNJbs"fDqU\8NvWd>MB}?`HHF=/ o+X i<cR8#\A6S(UV'(%8_]/7O70*/ao1,&[9C83St_Kj~[L}g mmY5O3ef~'3}:WI"j3i.)+{Bu q]y#9h 94.vfNOyp2H s9Ge:ZjiRj(bZ9oHYh%z~'e]{q QZG238dN,i Btr4kIE5L%3Ni wE^j {u;5.fe1Z N9.h1!;3I$cRHN >L@~A&pY 3Pb% N.i#eje~J]8nk\?yD7dhsL 1sw~S^jrQhg)qm6{|BWoY\\0WKzF_6t| _ "?=:k=\ v604zB.k|#OjpA/ OWzjgfQHqVgxW^Wm>;cDL{tx!~0U2[s\h-W[;!Y?;lB\t!c?VAy/gV.KJ7_N6+QL {81hP{Mm]D^#nsd17$we@c*{2F`[ 8nyo3.$ pE7LI(P2-~X {dym;S P`eYQ{?yjz-KH-3;#Jj9krk'#'C2"V$` :uAPhaDnfC;+Y aTEeEw!Z +[rR'=&kO5#gu%Iaj=>J 6O 8]2z0WiwWimy7%* 93XQ)}&?Z-D]}~{@dY&;)n8B9NB( @c|4YtwHlAshDq`obN$5@!Dnk"-`UtfKp{t>ro mt"N9xjv'i>'9tl5,emwe +X*v2J Ig$"QB6lz_B nBJ0m-J}ws+6z\:@-z{uMFShMo'tW/xtmbcx I9UWf(U8{ +0nOVj;D5_-,r&(b/S,-bvzSVE5[C[+ JY0=Y;=1TJLlAGlf'WUK^0e`tL~u9k 0u83xH & A < Z.=Wt Ri =oVu(0k%{h?K8:K+x'zKM'aWj#J`ULG%B$&qz`\ q( vbq\hI/1BCp<@6h_$m ^?gY&y+r ]cgURr^1*5*d$t B7MqxAV 3VUml f`zPsX7!&SES9um}x C!:Z5e7_@j=9SgVBz|w|ArA:+R(.Yge%}='mP.l~"}*5}:riV#Sp%O6sgVEwmJrKL?7"C'K  [HP ^+ervBY $Y-\-XUj]!wYG i%*^_m3S] $kq[H(l[KZvP$ eJHFRKF(hM"0*{5|ln< ?D;h|,Vwe|_2>@ y14t(59uGr ~L]TSJQ(T3zy4a*0jC:At\ K-'X3`} nAZ9sKB6%X%\V(W^-t[IM5DL6D)`$)EdM?TH<qP,d-8'NR"fG>Z:?rhumi9w2YN#aKs_tpka7 x4pp]bGJg[ueT# 6`y2|.}{Q\6hh j= %F`&[6B$ hKiG9j4H9[^.uSr`+tn/[J+L$W L?_zF0jCiSSNPXQ$MM_O_k?&@f5i~Zyg1^cveTB%1>${tFyC/@w*k[Gt4CguBzx,q[G@/]n*oa!%*k:Xh_&mCz"jL0R)+.I#7F::Y&yLO p!6- =)&WN/j}<;-:xdLsA |Z{xxe{'g?y{/)?vm&?(vCvS*a^nms{V=RiL'R@ZPxsS$ VTFISWdSgDY%KkI k|[pRN,)E Z~mbpX{'q|4/=i? +@32+,s)<|s) ]}a{|M W:bf 3kl2cwx3O.k&ai07y;ZB=my ;'/6}2SD(w1%n$vNcb2?8x3oaw$Tg[* Q9(R4~Zc$z l9M'HOV9G ,wytwQMQrwpE ^SbUrM+"$vk 4$p?&??\ |Jy\UCa{}4t GF;Q`gT3Sn+r11)GF^6 VSuo18sn*)sixjZ.&xa&r%H;Fy#7mEJ?Rcm<F?soOaY1dO"c:K Tp3;^qc~;5zQr4ef@}2- EC6E*$?U ] j-/NM2/U&]<RISg3kwG) .ysf_-2NI67(Vj{8CM ^I~f~UNch^Q/'XYSVu=;%3OV1RnZ<g}Yg0 q"CsTaxj 1>}s5sR]!p27D=B~E%D )'QRT{7wv}sE./h=& g?p&=<.7s49(JS~ x "GT/ ym>eWsZaOziF,yz](UM  #LMEc :}tSDQ{<-B#xRiqn Alw'4a"Q yusb"8PHDWyN o(zv.ZKnG;4QG_f  V!q=npbPbQa 6HPf SY8eCnOWd %8fdQLP/2P9(5SpYT2izxzu_*o Z^!H{Kt Alof e6ZU+wu>Y5!lK]> <m|.91rz#sd'J$\3$aD@aZ4w{AF7mt{vRqfDt6t%Qn  c8RV(g"?6Q_V&f}UI@`4L#Nl&9 NCXc{E-`g"=&AJE<?pr*r#  `.)3\h(*Cof   LiVTbo8W`tw.iB}l<Vz}cl>| F|r[<kESn vQ 4?i(Ku^ch6GhP$yE/R@3"4 ?A}9}m!%[yR^oo rRh[HYYWylY_ YpF}[E;>9&/Q V&y(<8.1@  "L+6s+[uxtWY\C6Y2GQxC$/%Y4hpHl8[GilByk Ly.xrmpV'IPivrddllYAx~# #  5:RGnUNU9~VmZ70xtfo,J{7]NKco;k+mR!Xol#_w lF",4ugvIy(Q483p4j7R*>5v<rB,~jZE,KegE.Q\u#e#V+n% 6u4eu5VeSu'>G8itP[t 7HO45FAsYLwUoS1GRD>`-AbstDHAa J;lMW-y*F4_P8vs_]?pQh9-=4Z9 +QN<E)WM2H@}!m[|mII]7l /:*)B,+O>dpcZQ'a{@ucwI`5*dh8v c!|]%y-W>a(*P{_CMiOrcJK'_;S5 (eF>Lif+ip0'2II8jg 1,%Mq=ebg7CkVD{bniMBF Vf)mR^L\g(- ( Q'V Qa9u}v!&%3Zcs`&)>FNvZ8x={yOQ A*-}o US89Nkx]NW7&yk#CAh#Vix~ Ak5R>kr> 5KbIu ml$H(MPQPq `XV6(Rk62hNAt "CZcpKGu!bt14}Ky?g/k>2ow TM- M38$*fp$,W6JfFz5<>y,Q(Qn_~;;tpYr)9r'uSY8VeL9JBKh;n3^~c.$>q7"*.L rwu7=_4q-]X+)6U]k*2QH8TIUr},$)4$f?3OIlkKi.lRs=&} sXEeS@ + Ox'%Xqq e `F+4L a#`Q"ZP!v<&THi)m1j.Ac1_:mliYm+(cc~er=wo%u%KY73OO-ljuvY*9/Rg9e^ @.lvrxK \dY^`e'[!Fgv49!!6xtX[uVhp>-GHGoS|ZV`?(2XC}yj=;C^Y [VVR8?ZG:) M.eA(cd .  Bw7"05&7{su`]!sp^W/u9;e29 LOnk4B Oet=Mvo3G:$0vW-N|V{)L:XzEuV13e,Vr#1 u[>I"nn*w0C_vFf;!Hm 2y Z)7|CHtr]t3OV(Ofd:Wf5k2|%K~U#kuM -cEvXmq_:3vMR@34NX-MpQ?W. G@ H1ryq]mD gju,RMXFj.13Mp_%|4Clu!Q2Q!?p3hQY*p4GGu1)AUScuvM:0r@*>zDT0(~>8!1$-?`n(W2sW*{oRZN=W p1$slSDI36qx^)jbsmh&,ao E(cN V<=EF-Aq&`bPhw#1i:BYf`Poa=Wvu M :JT!'WuPLzNhw cE#h;+ xI"pT]}3d 5*cA x"] ]!|KxaSxg*;4g4^ _ O'^IK-%e2Ocs.Oz( ^da+@4QB;jj!W\#~'vR?LJ3w"FlI}>1$l {Z7n@D3Rz1NJxb4v==Pe%Ji>?ENlbIF:n+0 <^bt+?[UGJK Y9gPe1 jYJ;r;0M@41[ dPtoxmXa#NbVp<fy3$zU|fw?Fx(06 d f+CmN!`#q) yw=kN#QRYA C*Bym@9  j5@+,)=TA`O\L8S[ga!G%175,;j'PoH6F-Y~Yw2jg=KiA1yD-_& `ry W+ft+NJpQC.Ru# 1jA;)]E f`Z=$LinB9QV' $Ty+tpW5c'`(I]$XG_*#1nY[?y.H0@1u.cZg8LmE9wEFV;@l)h0\RW|$NlTlf%P7%gsu:10`1-xe`QQa|rI: M?? $Vk >XOYqIXj+d.w` E" >UOMXp=B'o(O(@09e;^ZLPNOmzRIn|KFk} [F\|x5N2&yK%&nndH(~~/$Za_Q*^\MU6<w` 6SRCLDP7.3LDncrP>%fcq~8Ua`Pl?OT6}bgFo/]@u-ip62oY ; CdN!zl&91l.S dV7RS->`r9'P.d-xK"]LM5m=u5[@);QJr=gvJ0[E& h?j@!~A^S$XG^Yi!f&xC-,|g,6[<a2AK)6{Jq b8Y>d7ZE 7*?xs&lm. nk(g;u{kf!QPX&*R!2AU"*epSb_ 83 _w)vw[T3sVolcN<]gm:YRtJ`3NJyaK> L(C`S 2#X8kk0Q)bL{_n>JJ4e^\B>Z^!]h 2uDY6Qqg2ei 3 sAQ{n(s'^H*O<_|tZ<TEb/o9y"4$XNq,; +@ 1)>KG@R_d_{y_ MD \*OSV? %5YPQE\ y+ BE.4, H]"sJlFo2Ubz7vp"9B^F ySPaO]M'4#xsCA\(t5iuR\Y/H^fb9?['=ABA}CX@{yT 17R0 RMp-MQ]}"/2fO I&|@}1hAx%fvA=2U@O%PFZdxL<WwB!f{v:"Xfp^sYho+! ( \i=n"Ib%TT^`ucb@X(Yk}4O!\T }rt 8gIqr16Lxt*9BWw8ZRDyz8X}Y=tU'7wbjmn<&zqj*tp9w<JRFFVpTHr3YNPHN/O)2aOQtbWSA(%HpzRtn3 2%hw:nRQ'5w.-*U  :oNPEaOmGI<|#xbCZR%6'jKabeW/0F,eNBm: {0ujN\%OFR}PflH/aI -  ^8;)x2p[~=I{9j,q/N0'iqX}1sD\[G\hrWU t<4C/e'xJf<*UE(~G3:j]9D6o7x5'nixNnc**tG1c!~&26Kv4c3D V3nw1O^! q#A(ES J@E&e!LqueW/C6(-F0 $BxXgb|W].g ]4dJ5nRLrE:fLsx<=6~ ])o}vZm+gC&M1jL hfK:didPJ3T)11QtI:b+Kr::\+OF:1i'qDKZae;pe~nk6` [44-![es9_*RlhgUaG,wV@\*mRS[M$Y?$f3=Bxyd2*hH&.^x#6#+|7Ic.&7*6Jkm0hFdSa?n/2uw:+,5V^cy$T(0) <+g0F&R4j" V&>_rA6W8'T[jqx?V}!+D9$og=[bFYa<K`bZ;'4-}1ey#CylnJiT,m[NrI  ^$X _1T- Im-DS 8o*IQ[2+20` t^2$%\WOwUfAM2I pHokK-&l ?s^WCf&.w[q7%=D%8QCLLew\j :q*#p YZm.! AYJ> 5|8!_O;msM86 o*Kv8f(qZQ ,]^ekqTkt &nM$T_R=H&(3R+8l=E=Zm40/2HH6nha"`wf.7p1MSo@H&`$+v"C5jV!ipMb3e~FHv;@|<qXW`7UvsmV Jp|NPc(X]E%"HWjh# rDOnMV9Yk>XyK@p>k\ (,ggK %f1&C1 FWL41 #5VXjLnz)hZDo?[5X8tQt,0 o.P]E~i{XQsoZ4\8beWKlB3g hT_r></z0Bsb5. TFd1 e;iGG^*91`6j!"~nXe$I:h\`l^"UBA&f7B{RJrl6Z^Z858wl qOz|HD3|Lf] iCCoxZiFu'##9 +!kHlE9z _ W0 9hd-BMnv23+A7:-8r<|-f)F3)Ki{0g%CP2wJ/+knv9,8 St * _l#->+@,eIhYU0 E7GSxlQntu/;+v`_#_ =?mWv<avk=>z.}+PzC!DqmSMV-e+a>`L,NOlj5]b7 PR=w,^So9 1""FW' !3{) t56PQxsng c 9>F|4`ai+{("ghZ-B>0}T3(=n^J(Ti`Oj ql#H0MUD j(a9 OB1"9FXv 'SPPW. G'k)uVM wg*D7gn)?0cI\XbJ&^\|D1/YN%.^M5}a>d)Y|<$F&2)d+wY j=NlM Hi,32)kH_id\eFz?I@fPw@uqP Z 2]gPUE$ mONJtw %'i\ >KVS/@A#qJS9.Io-_]YAd>h{bLUpKm '*:OPTud.3dY,gXL"gJ!yMg8,7dqQEB}bs|/G(dnezNM{l' u+{/KA&nPqe]+8 .r$ 2wU[Z@( BR1*0G5A;iuJG#fpS"= @1zCcG:1nT][;d?|%h?okO]|Co: (x0|I<9:-OAi"rvlfByY kGkPT>4 hu2Tlf6q1./Tz`p/Z`w6fBm/rko%wNDL&wc>?^ g(`0TU0[,OhgyR# a-z_x< 1(5v$S`l*Y^Po@Nu L:4|Z("G. ]eU=kT{iq ;EQ'rd4X)OEH!4_M 1"D~V]TcGLHSu xC07| Ef]t$c 3t V\t5f`C?`FU;-a{c =&.iIW2"B+x!AmI<@ W} Xt_!7HV* Zosc |~z*~1W,] < *a8Gc: 1tO&s_G sla1%@RNvB?9d{)q^VoOD%}4-6, :zq|/ZCwr"?AqY{=?V$j Ffi F\IUg*%bWOd_J<4,u_zq -hG~lBD*( >F /a$@)u1Dk>}H^~ U)7Yk@>Fi7'd~r2]7Stkw~.,5w#a4;s)V/rMf88" aOA!x5jg"~H{ts{H% oU e~:|{r D^~573%1YiTF}1#5]FuBlbC]geySe*K,9*r19\KO!K]vhHa@ eNgpq-@mi{TC{) >?x?/C]D;m{)+J:TJ#TU1{S: F1.%& ~]jSL)9 %<"'o$|ha;C5+ 5fw@GG.Xqej-a~z@Il!"|V& t rp"sjc%`" >'O /T!$+y*sa1Ff:GnpV 1sf6#qt=&z*aZZWL>ry/_&&9r[. GRJ ~DW=w+=ZX&WdX`[ 8|[M,g I?rr~dH |OV%Aq|i?GT)1/:n[8W~81 z'\Fe4' +/COAv^]E U A1V=IU |j"l[LO` bC"=6 Kplu Cm#&3Xk+1j6p_ j7UY'B-1Gvq_UnV.O6G $2FIvnk7> '!g'j`7w"p$`<IGE"<9>|' wI+e}qyZPec2P6Oo:t>iaF 6<{sXk SJRpJK'q$eQ[~IPdl 3oxrv8Q50>[ /Xh4mm:^dOScMqDS iEx/#J.'jQ ( @tsBvKWLfH UmI}eh.C xF<[q)+]>.R8d",OH:;uSUQLA&Y-E2o"QfH\ApsieYBLC% #Dvky/w>j`qVGK_ npgSbez+:-l9~c`EC,n-IB>?rl*GB#|rv-dA\/r|[cn1Pgy>1Q4W{x*HA=f&ZFl;}eK"T-jO nPemO}G8*nMo68y p0y.[5ysp7z*!}Q|t&Se|fd?T!} vP/XFXeeGS7482nX[o+C,M3Z^EmkQ&-e&'(}\+$JQPCtyIvbRQLj[}cfn9 "kVZ\qtu;OZL8&Z ^&: sMr0e.NdCUkc"!-;-5,QA|p*q,p<% o\@ -D.~!7-q I2*p @yY2n@{=vaeahe_ AvtNAV-;YZjQ)PlAW?e L\3P2Z*9hhh{UjpQ(1]Oz"2''^OC|gCQa!OiNF&= j1wb~6]bX7+O }x2>2_`|VFOs$T+c~t\$E=N P9jQN)w;_$,$eT.2*6pjO->sV ,*$ Ln\ R--Yg7rGohSc?29 '  Hed1nS^gn\'kLQry "0X Ckbw4kST=fRAT3!j d;YL\YzwM[t%wk&  D-[ms9'ij`j|\3 | mGT; EUbhW8'oe}a "D\qe90wsikb\$Dg|'H w1yIl -NMWhX$o&d}i ;,df,S<}bME-IJX6?f@*Bq(t.qWK@7q ZOkc}KwVANr 5:\*~M*K| LavJ,> q6zilU T%n:=tM+xM #@f@9*f9jo U8IC3:H*I  99c6dxPv2` Hn?"rC^ }(&4ic5aqd(r D186c~@ >pmJ 8G\0/5V+}TTv) lH;}BJEV yfGS#!I:-9u]PJVZ/= % 7]PRDT5#V(Z)O%,'Bq0BW-~{"gf=+"LcdQ'lA]()u'q4o*@d=#P}G LptH$U__  As & kRzmi} !?QDC =Q[G`eZ'O _DW0P\c|6 }9G^Q-@FDp AqEYjoB#Ub:D7 dvmzRYIs])2'gs+jho dS"oPNxJG\\  t=koqfNBD4+N@  VtnF\w7kTV=]7Pg:x'zi {w|BcC*\Mj YTsc4kVY_1Q:d HK*BSEu'P*X?x3-tv -p= H8$Iz1j_ccMm|TI-,G{jIs6N"UtABV|?ZqZ*3 BNP6g_Ft52KjL veEQ2]>Ju}&z- q~_y "Na'[>BO[$>6np$$cP8QD[eqcc v1=s"z b(Ap& [KNnjwJ lQF/h5r-ij.4l sn="vrtp ={>]l{B:Cnq!V[|}/CXb_e.Ve-6]jjdv}[.lr}X:<hvKCO}  cL{?b{b:M>l0LT#ywV:x)(_p%-w_.9Ex%3DFb7<H.%FdxYsV{{T6f`H>qVP>`[7?$h| H&dx#GrnM}nCL5zUhqe4Bn /b-etPBu!.4,?AhD6-_wUE_-Cgk5d Y13S 45gm6HO{[ 5_!:h|S6E8nT?vF#g2d55CeAJ%0^mat90QmNGaY\{9fe9S2WH6NP%)K#BE ect 'J]}5-Ff+ ALgj|\f[=D[6f`9@4@uL) 59N2a|u6)||v|'dy/)_i6!c1'%~t4VO!9*jpiu\>VPKSwd[xp G1 aW2KnwlB YoMlFHrS+xUF*L69MT6xI X{"ZYu)5[O7t42. PBo -D7 VZ^0S<[E0Z56Df}Wc`zV.4]S6rSS^pM{8oumCi{54iNB'wYO e{y#?Hkf8XD]Ma:9giada-0.22.0/tests/samplePlayer.cpp000066400000000000000000000046371425106661500171640ustar00rootroot00000000000000#include "../src/core/channels/samplePlayer.h" #include TEST_CASE("SamplePlayer") { using namespace giada; constexpr int BUFFER_SIZE = 1024; constexpr int NUM_CHANNELS = 2; // Wave values: [1..BUFFERSIZE*4] m::Wave wave(0); wave.getBuffer().alloc(BUFFER_SIZE * 4, NUM_CHANNELS); wave.getBuffer().forEachFrame([](float* f, int i) { f[0] = static_cast(i + 1); f[1] = static_cast(i + 1); }); m::ChannelShared channelShared(BUFFER_SIZE); m::Resampler resampler(m::Resampler::Quality::LINEAR, NUM_CHANNELS); m::SamplePlayer samplePlayer(&resampler); samplePlayer.onLastFrame = [](bool) {}; SECTION("Test initialization") { REQUIRE(samplePlayer.hasWave() == false); } SECTION("Test rendering") { samplePlayer.loadWave(channelShared, &wave); REQUIRE(samplePlayer.hasWave() == true); REQUIRE(samplePlayer.begin == 0); REQUIRE(samplePlayer.end == wave.getBuffer().countFrames() - 1); REQUIRE(channelShared.tracker.load() == 0); REQUIRE(channelShared.playStatus.load() == ChannelStatus::OFF); for (const float pitch : {1.0f, 0.5f}) { samplePlayer.pitch = pitch; SECTION("Sub-range [M, N), pitch == " + std::to_string(pitch)) { constexpr int RANGE_BEGIN = 16; constexpr int RANGE_END = 48; samplePlayer.begin = RANGE_BEGIN; samplePlayer.end = RANGE_END; samplePlayer.render(channelShared, {}); int numFramesWritten = 0; channelShared.audioBuffer.forEachFrame([&numFramesWritten](float* f, int) { if (f[0] != 0.0) numFramesWritten++; }); REQUIRE(numFramesWritten == (RANGE_END - RANGE_BEGIN) / pitch); } SECTION("Rewind, pitch == " + std::to_string(pitch)) { // Point in audio buffer where the rewind takes place const int OFFSET = 256; samplePlayer.render(channelShared, {m::SamplePlayer::Render::Mode::REWIND, OFFSET}); // Rendering should start over again at buffer[OFFSET] REQUIRE(channelShared.audioBuffer[OFFSET][0] == 1.0f); } SECTION("Stop, pitch == " + std::to_string(pitch)) { // Point in audio buffer where the stop takes place const int OFFSET = 256; samplePlayer.render(channelShared, {m::SamplePlayer::Render::Mode::STOP, OFFSET}); int numFramesWritten = 0; channelShared.audioBuffer.forEachFrame([&numFramesWritten](float* f, int) { if (f[0] != 0.0) numFramesWritten++; }); REQUIRE(numFramesWritten == OFFSET); } } } } giada-0.22.0/tests/utils.cpp000066400000000000000000000043351425106661500156610ustar00rootroot00000000000000#include "../src/utils/fs.h" #include "../src/utils/math.h" #include "../src/utils/string.h" #include TEST_CASE("u::fs") { using namespace giada::u; REQUIRE(fs::fileExists(TEST_RESOURCES_DIR "test.wav") == true); REQUIRE(fs::fileExists("nonexistent_file") == false); REQUIRE(fs::dirExists(TEST_RESOURCES_DIR) == true); REQUIRE(fs::dirExists("ghost_dir/") == false); REQUIRE(fs::isDir(TEST_RESOURCES_DIR) == true); REQUIRE(fs::isDir("nonexistent_dir") == false); REQUIRE(fs::basename("tests/utils.cpp") == "utils.cpp"); REQUIRE(fs::dirname("tests/utils.cpp") == "tests"); REQUIRE(fs::getExt("tests/utils.cpp") == ".cpp"); REQUIRE(fs::stripExt("tests/utils.cpp") == "tests/utils"); #if defined(_WIN32) 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") == "C:\\"); REQUIRE(fs::getUpDir("C:\\") == "C:\\"); #else REQUIRE(fs::isRootDir("/") == true); REQUIRE(fs::isRootDir("/path/to/something") == false); REQUIRE(fs::getUpDir("/path/to/something") == "/path/to"); REQUIRE(fs::getUpDir("/path") == "/"); REQUIRE(fs::getUpDir("/") == "/"); #endif } TEST_CASE("u::string") { using namespace giada::u; REQUIRE(string::replace("Giada is cool", "cool", "hot") == "Giada is hot"); REQUIRE(string::trim(" Giada is cool ") == "Giada is cool"); REQUIRE(string::iToString(666) == "666"); REQUIRE(string::iToString(0x99AABB, true) == "99AABB"); REQUIRE(string::fToString(3.14159, 2) == "3.14"); REQUIRE(string::format("I see %d men with %s hats", 5, "strange") == "I see 5 men with strange hats"); std::vector v = string::split("Giada is cool", " "); REQUIRE(v.size() == 3); REQUIRE(v.at(0) == "Giada"); REQUIRE(v.at(1) == "is"); 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(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)); } giada-0.22.0/tests/wave.cpp000066400000000000000000000022421425106661500154560ustar00rootroot00000000000000#include "../src/core/wave.h" #include #include TEST_CASE("Wave") { using namespace giada; static const int SAMPLE_RATE = 44100; static const int BUFFER_SIZE = 4096; static const int CHANNELS = 2; static const int BIT_DEPTH = 32; /* Each SECTION the TEST_CASE is executed from the start. Any code between this comment and the first SECTION macro is executed before each SECTION. */ SECTION("test allocation") { m::Wave wave(1); wave.alloc(BUFFER_SIZE, CHANNELS, SAMPLE_RATE, BIT_DEPTH, "path/to/sample.wav"); SECTION("test basename") { REQUIRE(wave.getPath() == "path/to/sample.wav"); REQUIRE(wave.getBasename() == "sample"); REQUIRE(wave.getBasename(true) == "sample.wav"); } SECTION("test path") { wave.setPath("path/is/now/different.mp3"); REQUIRE(wave.getPath() == "path/is/now/different.mp3"); wave.setPath("path/is/now/different.mp3", 5); REQUIRE(wave.getPath() == "path/is/now/different-5.mp3"); } SECTION("test change name") { REQUIRE(wave.getPath() == "path/to/sample.wav"); REQUIRE(wave.getBasename() == "sample"); REQUIRE(wave.getBasename(true) == "sample.wav"); } } } giada-0.22.0/tests/waveFx.cpp000066400000000000000000000047261425106661500157650ustar00rootroot00000000000000#include "../src/core/waveFx.h" #include "../src/core/const.h" #include "../src/core/types.h" #include "../src/core/wave.h" #include #include using namespace giada; using namespace giada::m; TEST_CASE("waveFx") { static const int SAMPLE_RATE = 44100; static const int BUFFER_SIZE = 4000; static const int BIT_DEPTH = 32; Wave waveMono(0); Wave waveStereo(0); 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 = waveMono.getBuffer().countFrames(); 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 = waveStereo.getBuffer().countFrames(); REQUIRE(wfx::monoToStereo(waveStereo) == G_RES_OK); REQUIRE(waveStereo.getBuffer().countFrames() == prevSize); REQUIRE(waveStereo.getBuffer().countChannels() == 2); } } SECTION("test silence") { int a = 20; int b = 57; wfx::silence(waveStereo, a, b); 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 = waveStereo.getBuffer().countFrames(); wfx::cut(waveStereo, a, b); REQUIRE(waveStereo.getBuffer().countFrames() == prevSize - range); } SECTION("test trim") { int a = 47; int b = 210; int area = b - a; wfx::trim(waveStereo, a, b); REQUIRE(waveStereo.getBuffer().countFrames() == area); } SECTION("test fade") { int a = 47; int b = 500; wfx::fade(waveStereo, a, b, wfx::Fade::IN); wfx::fade(waveStereo, a, b, wfx::Fade::OUT); 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(waveStereo, a, b); 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); } } giada-0.22.0/tests/waveManager.cpp000066400000000000000000000035531425106661500167570ustar00rootroot00000000000000#include "../src/core/waveManager.h" #include "../src/core/const.h" #include "../src/core/wave.h" #include #include #include 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 this comment and the first SECTION macro is executed before each SECTION. */ WaveManager waveManager; SECTION("test creation") { WaveManager::Result res = waveManager.createFromFile(TEST_RESOURCES_DIR "test.wav", /*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->getBuffer().countChannels() == G_CHANNELS); REQUIRE(res.wave->isLogical() == false); REQUIRE(res.wave->isEdited() == false); } SECTION("test recording") { std::unique_ptr wave = waveManager.createEmpty(G_BUFFER_SIZE, G_MAX_IO_CHANS, G_SAMPLE_RATE, "test.wav"); REQUIRE(wave->getRate() == G_SAMPLE_RATE); 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); 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->getBuffer().countFrames() == oldSize * 2); REQUIRE(res.wave->getBuffer().countChannels() == G_CHANNELS); REQUIRE(res.wave->isLogical() == false); REQUIRE(res.wave->isEdited() == false); } } giada-0.22.0/tests/waveReader.cpp000066400000000000000000000034071425106661500166050ustar00rootroot00000000000000#include "../src/core/channels/waveReader.h" #include "../src/core/resampler.h" #include "../src/core/wave.h" #include "../src/utils/vector.h" #include #include TEST_CASE("WaveReader") { using namespace giada; constexpr int BUFFER_SIZE = 1024; constexpr int NUM_CHANNELS = 2; m::Wave wave(0); wave.getBuffer().alloc(BUFFER_SIZE, NUM_CHANNELS); wave.getBuffer().forEachFrame([](float* f, int i) { f[0] = static_cast(i + 1); f[1] = static_cast(i + 1); }); m::Resampler resampler; m::WaveReader waveReader(&resampler); SECTION("Test initialization") { REQUIRE(waveReader.wave == nullptr); } waveReader.wave = &wave; SECTION("Test fill, pitch 1.0") { mcl::AudioBuffer out(BUFFER_SIZE, NUM_CHANNELS); SECTION("Regular fill") { m::WaveReader::Result res = waveReader.fill(out, /*start=*/0, BUFFER_SIZE, /*offset=*/0, /*pitch=*/1.0f); bool allFilled = true; int numFramesFilled = 0; out.forEachFrame([&allFilled, &numFramesFilled](const float* f, int) { if (f[0] == 0.0f) allFilled = false; else numFramesFilled++; }); REQUIRE(allFilled); REQUIRE(numFramesFilled == res.used); REQUIRE(numFramesFilled == res.generated); } SECTION("Partial fill") { m::WaveReader::Result res = waveReader.fill(out, /*start=*/0, BUFFER_SIZE, /*offset=*/BUFFER_SIZE / 2, /*pitch=*/1.0f); int numFramesFilled = 0; out.forEachFrame([&numFramesFilled](const float* f, int) { if (f[0] != 0.0f) numFramesFilled++; }); REQUIRE(numFramesFilled == BUFFER_SIZE / 2); REQUIRE(out[(BUFFER_SIZE / 2) - 1][0] == 0.0f); REQUIRE(out[BUFFER_SIZE / 2][0] != 0.0f); REQUIRE(numFramesFilled == res.used); REQUIRE(numFramesFilled == res.generated); } } } giada-0.22.0/vcpkg.json000066400000000000000000000013351425106661500146550ustar00rootroot00000000000000{ "name": "giada", "version-string": "1.0", "builtin-baseline": "af2287382b1991dbdcb7e5112d236f3323b9dd7a", "dependencies": [ { "name": "rtmidi", "version>=": "5.0.0" }, { "name": "fmt", "version>=": "8.1.1" }, { "name": "fltk", "version>=": "1.3.8" }, { "name": "catch2", "version>=": "2.13.8" }, { "name": "libsamplerate", "version>=": "0.2.2" }, { "name": "libsndfile", "version>=": "1.0.31", "features": [ "external-libs" ] } ] }