--- /dev/null
+# ------------------------------------------------------------------------------
+# 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/core/midiDispatcher.cpp
+ src/core/midiMapConf.cpp
+ src/core/midiEvent.cpp
+ src/core/audioBuffer.cpp
+ src/core/quantizer.cpp
+ src/core/conf.cpp
+ src/core/kernelAudio.cpp
+ src/core/mixerHandler.cpp
+ src/core/sequencer.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/recorderHandler.cpp
+ src/core/recorder.cpp
+ src/core/mixer.cpp
+ src/core/clock.cpp
+ src/core/waveManager.cpp
+ src/core/recManager.cpp
+ src/core/midiLearnParam.cpp
+ src/core/plugins/pluginHost.cpp
+ src/core/plugins/pluginManager.cpp
+ src/core/plugins/plugin.cpp
+ src/core/plugins/pluginState.cpp
+ src/core/channels/state.cpp
+ src/core/channels/sampleActionRecorder.cpp
+ src/core/channels/midiActionRecorder.cpp
+ src/core/channels/waveReader.cpp
+ src/core/channels/midiController.cpp
+ src/core/channels/sampleController.cpp
+ src/core/channels/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/channelManager.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/gui/dialogs/window.cpp
+ src/gui/dispatcher.cpp
+ src/gui/updater.cpp
+ src/gui/model.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/devInfo.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/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/browser.cpp
+ src/gui/elems/soundMeter.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/boostTool.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/noteEditor.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/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/beatMeter.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/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/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/radio.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)
+
+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)
+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(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)
+
+find_package(Threads REQUIRED)
+list(APPEND LIBRARIES ${Threads_LIBRARY})
+
+# RtMidi
+
+find_package(RtMidi CONFIG)
+if (RtMidi_FOUND)
+ list(APPEND LIBRARIES RtMidi::rtmidi)
+else()
+ # 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()
+
+ # RtMidi header path may vary accross 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})
+endif()
+
+message("RtMidi library found in " ${RtMidi_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 CONFIG REQUIRED)
+list(APPEND LIBRARIES fltk fltk_gl fltk_forms fltk_images)
+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})
+else()
+ # 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
+# TODO - new libsamplerate now provides CMake targets. Update it!
+
+find_library(LIBRARY_SAMPLERATE
+ NAMES samplerate libsamplerate libsamplerate-0 liblibsamplerate-0
+ PATHS ${_VCPKG_ROOT_DIR}/installed/${VCPKG_TARGET_TRIPLET})
+list(APPEND LIBRARIES ${LIBRARY_SAMPLERATE})
+message("Libsamplerate library found in " ${LIBRARY_SAMPLERATE})
+
+# 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)
+ 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} ${ALSA_LIBRARIES} pthread stdc++fs)
+
+ list(APPEND PREPROCESSOR_DEFS
+ WITH_AUDIO_JACK
+ __LINUX_ALSA__
+ __LINUX_PULSE__
+ __UNIX_JACK__)
+
+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
+ __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_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
+# ------------------------------------------------------------------------------
+
+install(TARGETS giada DESTINATION ${CMAKE_INSTALL_PREFIX})
+
+# ------------------------------------------------------------------------------
+# Extra
+# ------------------------------------------------------------------------------
+
+# Enable static linking of the MSVC runtime library on Windows
+# TODO - move this into the 'if windows' conditional (needs smarter list first)
+
+if(DEFINED OS_WINDOWS)
+ set_target_properties(giada PROPERTIES
+ MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
+endif()
--------------------------------------------------------------------------------
+0.17.1 --- 2021 . 02 . 01
+- Better CMake dependency management
+- Add CMake install rules (#422)
+- Switch to GitHub Actions for CI and release builds (#440)
+- Remove hardcored 'test' folder in test suite (#432)
+- 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
cppFlags = -I$(top_srcdir)/src
-cxxFlags = -std=c++14 -Wall
+cxxFlags = -std=c++17 -Wall
ldAdd =
ldFlags =
sourcesExtra =
sourcesCore = \
src/core/const.h \
src/core/queue.h \
+ src/core/ringBuffer.h \
src/core/types.h \
src/core/range.h \
src/core/action.h \
src/core/midiEvent.cpp \
src/core/audioBuffer.h \
src/core/audioBuffer.cpp \
+ src/core/quantizer.h \
+ src/core/quantizer.cpp \
src/core/conf.h \
src/core/conf.cpp \
src/core/kernelAudio.h \
src/core/kernelAudio.cpp \
- src/core/pluginHost.h \
- src/core/pluginHost.cpp \
- src/core/pluginManager.h \
- src/core/pluginManager.cpp \
+ src/core/plugins/pluginHost.h \
+ src/core/plugins/pluginHost.cpp \
+ src/core/plugins/pluginManager.h \
+ src/core/plugins/pluginManager.cpp \
+ src/core/plugins/plugin.h \
+ src/core/plugins/plugin.cpp \
+ src/core/plugins/pluginState.h \
+ src/core/plugins/pluginState.cpp \
src/core/mixerHandler.h \
src/core/mixerHandler.cpp \
+ src/core/sequencer.h \
+ src/core/sequencer.cpp \
src/core/init.h \
src/core/init.cpp \
- src/core/plugin.h \
- src/core/plugin.cpp \
src/core/wave.h \
src/core/wave.cpp \
src/core/waveFx.h \
src/core/kernelMidi.cpp \
src/core/graphics.h \
src/core/graphics.cpp \
+ src/core/midiLearnParam.cpp \
src/core/patch.h \
src/core/patch.cpp \
src/core/recorderHandler.h \
src/core/waveManager.cpp \
src/core/recManager.h \
src/core/recManager.cpp \
+ src/core/channels/state.h \
+ src/core/channels/state.cpp \
+ src/core/channels/sampleActionRecorder.h \
+ src/core/channels/sampleActionRecorder.cpp \
+ src/core/channels/midiActionRecorder.h \
+ src/core/channels/midiActionRecorder.cpp \
+ src/core/channels/waveReader.h \
+ src/core/channels/waveReader.cpp \
+ src/core/channels/midiController.h \
+ src/core/channels/midiController.cpp \
+ src/core/channels/sampleController.h \
+ src/core/channels/sampleController.cpp \
+ src/core/channels/samplePlayer.h \
+ src/core/channels/samplePlayer.cpp \
+ src/core/channels/audioReceiver.h \
+ src/core/channels/audioReceiver.cpp \
+ src/core/channels/midiLighter.h \
+ src/core/channels/midiLighter.cpp \
+ src/core/channels/midiLearner.h \
+ src/core/channels/midiLearner.cpp \
+ src/core/channels/midiSender.h \
+ src/core/channels/midiSender.cpp \
+ src/core/channels/midiReceiver.h \
+ src/core/channels/midiReceiver.cpp \
src/core/channels/channel.h \
src/core/channels/channel.cpp \
- src/core/channels/midiChannel.h \
- src/core/channels/midiChannel.cpp \
- src/core/channels/masterChannel.h \
- src/core/channels/masterChannel.cpp \
- src/core/channels/sampleChannel.h \
- src/core/channels/sampleChannel.cpp \
src/core/channels/channelManager.h \
src/core/channels/channelManager.cpp \
- src/core/channels/sampleChannelProc.h \
- src/core/channels/sampleChannelProc.cpp \
- src/core/channels/sampleChannelRec.h \
- src/core/channels/sampleChannelRec.cpp \
- src/core/channels/midiChannelProc.h \
- src/core/channels/midiChannelProc.cpp \
src/core/model/model.h \
src/core/model/model.cpp \
src/core/model/storage.h \
src/core/model/storage.cpp \
src/core/idManager.h \
src/core/idManager.cpp \
+ src/glue/events.h \
+ src/glue/events.cpp \
src/glue/main.h \
src/glue/main.cpp \
src/glue/io.h \
src/gui/dialogs/midiIO/midiInputChannel.cpp \
src/gui/dialogs/midiIO/midiInputMaster.h \
src/gui/dialogs/midiIO/midiInputMaster.cpp \
+ src/gui/elems/midiIO/midiLearner.h \
+ src/gui/elems/midiIO/midiLearner.cpp \
+ src/gui/elems/midiIO/midiLearnerPack.h \
+ src/gui/elems/midiIO/midiLearnerPack.cpp \
src/gui/elems/browser.h \
src/gui/elems/browser.cpp \
src/gui/elems/soundMeter.h \
src/gui/elems/actionEditor/envelopePoint.cpp \
src/gui/elems/actionEditor/pianoRoll.h \
src/gui/elems/actionEditor/pianoRoll.cpp \
- src/gui/elems/actionEditor/noteEditor.h \
+ src/gui/elems/actionEditor/noteEditor.h \
src/gui/elems/actionEditor/noteEditor.cpp \
src/gui/elems/actionEditor/pianoItem.h \
src/gui/elems/actionEditor/pianoItem.cpp \
src/gui/elems/config/tabBehaviors.cpp \
src/gui/elems/config/tabPlugins.h \
src/gui/elems/config/tabPlugins.cpp \
- src/gui/elems/midiIO/midiLearnerBase.h \
- src/gui/elems/midiIO/midiLearnerBase.cpp \
- src/gui/elems/midiIO/midiLearnerMaster.h \
- src/gui/elems/midiIO/midiLearnerMaster.cpp \
- src/gui/elems/midiIO/midiLearnerChannel.h \
- src/gui/elems/midiIO/midiLearnerChannel.cpp \
- src/gui/elems/midiIO/midiLearnerPlugin.h \
- src/gui/elems/midiIO/midiLearnerPlugin.cpp \
src/gui/elems/basics/scroll.h \
src/gui/elems/basics/scroll.cpp \
+ src/gui/elems/basics/pack.h \
+ src/gui/elems/basics/pack.cpp \
+ src/gui/elems/basics/group.h \
+ src/gui/elems/basics/group.cpp \
+ src/gui/elems/basics/scrollPack.h \
+ src/gui/elems/basics/scrollPack.cpp \
src/gui/elems/basics/boxtypes.h \
src/gui/elems/basics/boxtypes.cpp \
- src/gui/elems/basics/baseButton.h \
- src/gui/elems/basics/baseButton.cpp \
src/gui/elems/basics/statusButton.h \
src/gui/elems/basics/statusButton.cpp \
src/gui/elems/basics/button.h \
src/utils/ver.cpp \
src/utils/string.h \
src/utils/string.cpp \
- src/deps/rtaudio/RtAudio.h \
+ src/deps/rtaudio/RtAudio.h \
src/deps/rtaudio/RtAudio.cpp
sourcesTests = \
tests/main.cpp \
tests/utils.cpp \
tests/recorder.cpp \
tests/waveFx.cpp \
- tests/audioBuffer.cpp \
- tests/sampleChannel.cpp
-
+ tests/audioBuffer.cpp
if WITH_VST
sourcesExtra += \
cxxFlags += -ObjC++
ldAdd += -lsndfile -lfltk -lrtmidi -lsamplerate -lm -lpthread \
- -lFLAC -logg -lvorbis -lvorbisenc
+ -lFLAC -logg -lvorbis -lvorbisenc -lopus
ldFlags += -framework CoreAudio -framework Cocoa -framework Carbon \
-framework CoreMIDI -framework CoreFoundation -framework Accelerate \
bin_PROGRAMS = giada
-giada_SOURCES = $(sourcesCore) $(sourcesMain) $(sourcesExtra)
+giada_SOURCES = $(sourcesCore) $(sourcesExtra) $(sourcesMain)
giada_CPPFLAGS = $(cppFlags)
giada_CXXFLAGS = $(cxxFlags)
giada_LDADD = $(ldAdd)
</p>
<p align="center">
-<strong>Giada - Your Hardcore Loop Machine</strong> | Official website: <a href="https://www.giadamusic.com">giadamusic.com</a> | Travis CI status: <a href="https://travis-ci.org/monocasual/giada"><img src="https://travis-ci.org/monocasual/giada.svg?branch=master" alt="Build status"></a>
+<strong>Giada - Your Hardcore Loop Machine</strong> | Official website: <a href="https://www.giadamusic.com">giadamusic.com</a> | <a href="https://github.com/monocasual/giada/actions?query=workflow%3A%22Continuous+integration%22"><img src="https://github.com/monocasual/giada/workflows/Continuous%20integration/badge.svg" alt="Build status"></a>
</p>
## What is Giada?
-Giada is a free, minimal, hardcore audio tool for DJs, live performers and electronic musicians. How does it work? Just pick up your channel, fill it with samples or MIDI events and start the show by using this tiny piece of software as a loop machine, drum machine, sequencer, live sampler or yet as a plugin/effect host. Giada aims to be a compact and portable virtual device for Linux, Mac OS X and Windows for production use and live sets.
+Giada is an open source, minimalistic and hardcore music production tool. Designed for DJs, live performers and electronic musicians.
<p align="center">
✦✦✦ <a href="http://www.youtube.com/user/GiadaLoopMachine">See Giada in action!</a> ✦✦✦
</p>
-
+
## 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;
-* high quality internal resampler;
-* unlimited number of channels (controllable via computer keyboard);
-* several playback modes and combinations;
+* unlimited number of channels (optionally controllable via computer keyboard);
* BPM and beat sync with sample-accurate loop engine;
-* VST and VSTi (instrument) plug-in support;
-* MIDI input and output support, featuring custom [MIDI lightning messages](https://github.com/monocasual/giada-midimaps);
-* super-sleek, built-in wave editor;
-* live sampler from external inputs;
-* live action recorder with automatic quantizer;
-* piano Roll editor;
-* portable patch storage system, based on super-hackable JSON files;
+* MIDI output support, featuring custom [MIDI lightning messages](https://github.com/monocasual/giada-midimaps);
+* super-sleek, built-in Wave Editor for audio samples and Piano Roll editor for MIDI messages;
+* automatic quantizer;
+* portable project storage system, based on super-hackable JSON files;
* support for all major uncompressed file formats;
* test-driven development style supported by [Travis CI](https://travis-ci.org/monocasual/giada) and [Catch](https://github.com/philsquared/Catch)
* under a constant stage of development;
AC_ARG_ENABLE(
[vst],
AS_HELP_STRING([--enable-vst], [enable vst support]),
- [AC_DEFINE(WITH_VST) AM_CONDITIONAL(WITH_VST, true)],
+ [AC_DEFINE(WITH_VST) AM_CONDITIONAL(WITH_VST, true)],
[AM_CONDITIONAL(WITH_VST, false)]
)
AC_ARG_ENABLE(
[system-catch],
AS_HELP_STRING([--enable-system-catch], [use system-provided Catch library]),
- [AC_DEFINE(WITH_SYSTEM_CATCH) AM_CONDITIONAL(WITH_SYSTEM_CATCH, true)],
+ [AC_DEFINE(WITH_SYSTEM_CATCH) AM_CONDITIONAL(WITH_SYSTEM_CATCH, true)],
[AM_CONDITIONAL(WITH_SYSTEM_CATCH, false)]
)
AC_ARG_ENABLE(
[debug],
AS_HELP_STRING([--enable-debug], [enable debug mode (asserts, ...)]),
- [],
+ [],
[AC_DEFINE(NDEBUG)]
)
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Creator: CorelDRAW X7 -->
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" style="image-rendering:optimizeQuality;text-rendering:geometricPrecision;shape-rendering:geometricPrecision" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" xml:space="preserve" height="1.557in" width="1.557in" version="1.1" clip-rule="evenodd" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1557.0001 1556.9888" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/><dc:title/></cc:Work></rdf:RDF></metadata><defs><style type="text/css"><![CDATA[
+ .str0 {stroke:#D2D3D5;stroke-width:6.94488}
+ .fil2 {fill:#FEFEFE}
+ .fil0 {fill:#FEFEFE}
+ .fil11 {fill:#E6E7E8}
+ .fil5 {fill:#4B4B4D}
+ .fil1 {fill:#454545}
+ .fil4 {fill:#E01C26}
+ .fil10 {fill:#E01C26}
+ .fil7 {fill:url(#id0)}
+ .fil8 {fill:url(#id1)}
+ .fil3 {fill:url(#id2)}
+ .fil6 {fill:url(#id3)}
+ .fil9 {fill:url(#id4)}
+ ]]></style><linearGradient id="id0" y2="14445" gradientUnits="userSpaceOnUse" x2="11848" y1="14445" x1="10647"><stop stop-color="#FEFEFE" offset="0"/><stop stop-color="#fff" offset="1"/></linearGradient><linearGradient id="id1" y2="12968" gradientUnits="userSpaceOnUse" x2="2744.5" y1="12032" x1="2042.5"><stop stop-color="#E01C26" offset="0"/><stop stop-color="#822F2F" offset="1"/></linearGradient><linearGradient id="id2" y2="10789" xlink:href="#id1" gradientUnits="userSpaceOnUse" x2="11599" y1="9853.5" x1="10897"/><linearGradient id="id3" y2="14913" xlink:href="#id1" gradientUnits="userSpaceOnUse" x2="11599" y1="13978" x1="10897"/><linearGradient id="id4" y2="22898" gradientUnits="userSpaceOnUse" x2="2500.9" y1="17663" x1="7754.9"><stop stop-color="#E01C26" offset="0"/><stop stop-color="#993132" offset="1"/></linearGradient></defs><g transform="translate(-1615 -3941)"><path d="m2394 3941c430 0 778 348 778 779 0 430-348 778-778 778-431 0-779-348-779-778 0-431 348-779 779-779z" class="fil1" fill="#454545"/><path d="m2394 4362c197 0 357 160 357 358v2h11c91 0 171 43 222 110 7-36 10-74 10-112 0-332-269-601-600-601-332 0-601 269-601 601 0 331 269 600 601 600 89 0 175-20 251-55 27 10 56 15 86 15h17c126-9 227-114 227-243 0-48-14-92-37-130-44-68-120-114-207-114-135 0-244 109-244 244 0 9 0 18 1 27-30 8-62 13-94 13-198 0-358-160-358-357 0-198 160-358 358-358zm151 263c-32-50-88-84-151-84-99 0-179 80-179 179 0 98 80 178 179 178 4 0 8 0 12-1 93-6 166-83 166-177 0-35-10-68-27-95z" class="fil2" fill="#fefefe"/></g></svg>
--- /dev/null
+#!/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 <version tag> -s <signature email or key ID>"
+ 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:
-#include <new>
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
#include <cassert>
-#include <cstring>
+#include <algorithm>
#include "audioBuffer.h"
namespace m
{
AudioBuffer::AudioBuffer()
- : m_data (nullptr),
- m_size (0),
- m_channels(0)
+: m_data (nullptr)
+, m_size (0)
+, m_channels(0)
+{
+}
+
+
+AudioBuffer::AudioBuffer(Frame size, int channels)
+: AudioBuffer()
{
+ alloc(size, channels);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+AudioBuffer::AudioBuffer(const AudioBuffer& o)
+: m_data (new float[o.m_size * o.m_channels])
+, m_size (o.m_size)
+, m_channels(o.m_channels)
+{
+ std::copy(o.m_data, o.m_data + (o.m_size * o.m_channels), m_data);
}
/* -------------------------------------------------------------------------- */
-float* AudioBuffer::operator [](int offset) const
+float* AudioBuffer::operator [](Frame offset) const
{
assert(m_data != nullptr);
assert(offset < m_size);
/* -------------------------------------------------------------------------- */
-void AudioBuffer::clear(int a, int b)
+void AudioBuffer::clear(Frame a, Frame b)
{
if (m_data == nullptr)
return;
if (b == -1) b = m_size;
- memset(m_data + (a * m_channels), 0, (b - a) * m_channels * sizeof(float));
+ std::fill_n(m_data + (a * m_channels), (b - a) * m_channels, 0.0);
}
/* -------------------------------------------------------------------------- */
-int AudioBuffer::countFrames() const { return m_size; }
-int AudioBuffer::countSamples() const { return m_size * m_channels; }
-int AudioBuffer::countChannels() const { return m_channels; }
-bool AudioBuffer::isAllocd() const { return m_data != nullptr; }
+Frame AudioBuffer::countFrames() const { return m_size; }
+int AudioBuffer::countSamples() const { return m_size * m_channels; }
+int AudioBuffer::countChannels() const { return m_channels; }
+bool AudioBuffer::isAllocd() const { return m_data != nullptr; }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float AudioBuffer::getPeak() const
+{
+ float peak = 0.0f;
+ for (int i = 0; i < countSamples(); i++)
+ peak = std::max(peak, m_data[i]);
+ return peak;
+}
/* -------------------------------------------------------------------------- */
-void AudioBuffer::alloc(int size, int channels)
+void AudioBuffer::alloc(Frame size, int channels)
{
+ assert(channels <= NUM_CHANS);
+
free();
m_size = size;
m_channels = channels;
m_data = new float[m_size * m_channels];
- clear(); // does nothing if m_data == nullptr
+ clear();
}
void AudioBuffer::free()
{
- delete[] m_data; // No check required, delete nullptr does nothing
+ delete[] m_data;
setData(nullptr, 0, 0);
}
/* -------------------------------------------------------------------------- */
-void AudioBuffer::setData(float* data, int size, int channels)
+void AudioBuffer::setData(float* data, Frame size, int channels)
{
+ assert(channels <= NUM_CHANS);
+
m_data = data;
m_size = size;
m_channels = channels;
void AudioBuffer::moveData(AudioBuffer& b)
{
+ assert(b.countChannels() <= NUM_CHANS);
+
free();
m_data = b[0];
m_size = b.countFrames();
/* -------------------------------------------------------------------------- */
-void AudioBuffer::copyFrame(int frame, float* values)
+void AudioBuffer::copyData(const float* data, Frame frames, int channels, int offset)
{
assert(m_data != nullptr);
- memcpy(m_data + (frame * m_channels), values, m_channels * sizeof(float));
+ assert(frames <= m_size - offset);
+
+ if (channels < NUM_CHANS) // i.e. one channel, mono
+ for (int i = offset, k = 0; i < m_size; i++, k++)
+ for (int j = 0; j < countChannels(); j++)
+ (*this)[i][j] = data[k];
+ else
+ if (channels == NUM_CHANS)
+ std::copy_n(data, frames * channels, m_data + (offset * channels));
+ else
+ assert(false);
+}
+
+
+void AudioBuffer::copyData(const AudioBuffer& b, float gain)
+{
+ copyData(b[0], b.countFrames(), b.countChannels());
+ if (gain != 1.0f)
+ applyGain(gain);
}
/* -------------------------------------------------------------------------- */
-void AudioBuffer::copyData(const float* data, int frames, int offset)
+
+void AudioBuffer::addData(const AudioBuffer& b, float gain, Pan pan)
{
assert(m_data != nullptr);
- assert(frames <= m_size - offset);
- memcpy(m_data + (offset * m_channels), data, frames * m_channels * sizeof(float));
+ assert(countFrames() <= b.countFrames());
+ assert(b.countChannels() <= NUM_CHANS);
+
+ for (int i = 0; i < countFrames(); i++)
+ for (int j = 0; j < countChannels(); j++)
+ (*this)[i][j] += b[i][j] * gain * pan[j];
}
+
+/* -------------------------------------------------------------------------- */
+
+
+void AudioBuffer::applyGain(float g)
+{
+ for (int i = 0; i < countSamples(); i++)
+ m_data[i] *= g;
+}
}} // giada::m::
\ No newline at end of file
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
#ifndef G_AUDIO_BUFFER_H
#define G_AUDIO_BUFFER_H
+#include <array>
+#include "core/types.h"
+
+
namespace giada {
namespace m
{
-/* TODO - this class needs a serious modern C++ lifting */
+/* AudioBuffer
+A class that holds a buffer filled with audio data. NOTE: currently it only
+supports 2 channels (stereo). Give it a mono stream and it will convert it to
+stereo. Give it a multichannel stream and it will throw an assertion. */
+
class AudioBuffer
{
public:
+
+ static constexpr int NUM_CHANS = 2;
+
+ using Pan = std::array<float, NUM_CHANS>;
+
+ /* AudioBuffer (1)
+ Creates an empty (and invalid) audio buffer. */
AudioBuffer();
+
+ /* AudioBuffer (2)
+ Creates an audio buffer and allocates memory for size * channels frames. */
+
+ AudioBuffer(Frame size, int channels);
+
+ AudioBuffer(const AudioBuffer& o);
~AudioBuffer();
/* operator []
float* operator [](int offset) const;
- int countFrames() const;
+ Frame countFrames() const;
int countSamples() const;
int countChannels() const;
bool isAllocd() const;
- void alloc(int size, int channels);
+ /* getPeak
+ Returns the highest value from any channel. */
+
+ float getPeak() const;
+
+ void alloc(Frame size, int channels);
void free();
- /* copyData
+ /* copyData (1)
Copies 'frames' frames from the new 'data' into m_data, and fills m_data
- starting from frame 'offset'. It takes for granted that the new data contains
- the same number of channels than m_channels. */
+ starting from frame 'offset'. The new data MUST NOT contain more than
+ NUM_CHANS channels. If channels < NUM_CHANS, they will be spread over the
+ stereo buffer. */
+
+ void copyData(const float* data, Frame frames, int channels=NUM_CHANS, int offset=0);
- void copyData(const float* data, int frames, int offset=0);
+ /* copyData (2)
+ Copies buffer 'b' onto this one. If 'b' has less channels than this one,
+ they will be spread over the current ones. Buffer 'b' MUST NOT contain more
+ channels than this one. */
- /* copyFrame
- Copies data pointed by 'values' into m_data[frame]. It takes for granted that
- 'values' contains the same number of channels than m_channels. */
+ void copyData(const AudioBuffer& b, float gain=1.0f);
- void copyFrame(int frame, float* values);
+ /* addData
+ Merges audio data from buffer 'b' onto this one. Applies optional gain and
+ pan if needed. */
+
+ void addData(const AudioBuffer& b, float gain=1.0f, Pan pan={1.0f, 1.0f});
/* setData
- Borrow 'data' as new m_data. Makes sure not to delete the data 'data' points
+ Views 'data' as new m_data. Makes sure not to delete the data 'data' points
to while using it. Set it back to nullptr when done. */
- void setData(float* data, int size, int channels);
+ void setData(float* data, Frame size, int channels);
/* moveData
- Moves data held by 'b' into this buffer. Then 'b' becomes an empty buffer. */
+ Moves data held by 'b' into this buffer. Then 'b' becomes an empty buffer.
+ TODO - add move constructor instead! */
void moveData(AudioBuffer& b);
Clears the internal data by setting all bytes to 0.0f. Optional parameters
'a' and 'b' set the range. */
- void clear(int a=0, int b=-1);
+ void clear(Frame a=0, Frame b=-1);
+
+ void applyGain(float g);
private:
float* m_data;
- int m_size; // in frames
+ Frame m_size;
int m_channels;
};
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "core/channels/state.h"
+#include "audioReceiver.h"
+
+
+namespace giada {
+namespace m
+{
+AudioReceiver::AudioReceiver(ChannelState* c, const conf::Conf& conf)
+: state (std::make_unique<AudioReceiverState>(conf))
+, m_channelState(c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+AudioReceiver::AudioReceiver(const patch::Channel& p, ChannelState* c)
+: state (std::make_unique<AudioReceiverState>(p))
+, m_channelState(c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+AudioReceiver::AudioReceiver(const AudioReceiver& o, ChannelState* c)
+: state (std::make_unique<AudioReceiverState>(*o.state))
+, m_channelState(c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void AudioReceiver::render(const AudioBuffer& in) const
+{
+ /* If armed and input monitor is on, copy input buffer to channel buffer:
+ this enables the input monitoring. The channel buffer will be overwritten
+ later on by pluginHost::processStack, so that you would record "clean" audio
+ (i.e. not plugin-processed). */
+
+ bool armed = m_channelState->armed.load();
+ bool inputMonitor = state->inputMonitor.load();
+
+ if (armed && inputMonitor)
+ m_channelState->buffer.addData(in); // add, don't overwrite
+}
+}} // giada::m::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_CHANNEL_AUDIO_RECEIVER_H
+#define G_CHANNEL_AUDIO_RECEIVER_H
+
+
+#include <memory>
+
+
+namespace giada {
+namespace m
+{
+namespace conf
+{
+struct Conf;
+}
+namespace patch
+{
+struct Channel;
+}
+class AudioBuffer;
+struct ChannelState;
+struct AudioReceiverState;
+
+/* AudioReceiver
+Operates on input audio streams for audio recording and input monitor. */
+
+class AudioReceiver
+{
+public:
+
+ AudioReceiver(ChannelState*, const conf::Conf&);
+ AudioReceiver(const patch::Channel&, ChannelState*);
+ AudioReceiver(const AudioReceiver&, ChannelState* c=nullptr);
+
+ void render(const AudioBuffer& in) const;
+
+ /* state
+ Pointer to mutable AudioReceiverState state. */
+
+ std::unique_ptr<AudioReceiverState> state;
+
+private:
+
+ ChannelState* m_channelState;
+};
+}} // giada::m::
+
+
+#endif
#include <cassert>
-#include "utils/log.h"
-#include "core/channels/channelManager.h"
-#include "core/const.h"
-#include "core/pluginManager.h"
-#include "core/plugin.h"
-#include "core/kernelMidi.h"
-#include "core/patch.h"
-#include "core/clock.h"
-#include "core/wave.h"
-#include "core/mixer.h"
+#include "core/channels/state.h"
#include "core/mixerHandler.h"
-#include "core/recorderHandler.h"
-#include "core/conf.h"
-#include "core/patch.h"
-#include "core/waveFx.h"
-#include "core/midiMapConf.h"
+#include "core/plugins/pluginHost.h"
#include "channel.h"
namespace giada {
namespace m
{
-Channel::Channel(ChannelType type, ChannelStatus playStatus, int bufferSize,
- ID columnId, ID id)
-: type (type),
- playStatus (playStatus),
- recStatus (ChannelStatus::OFF),
- columnId (columnId),
- id (id),
- height (G_GUI_UNIT),
- previewMode (PreviewMode::NONE),
- pan (0.5f),
- volume (G_DEFAULT_VOL),
- armed (false),
- key (0),
- mute (false),
- solo (false),
- volume_i (1.0f),
- volume_d (0.0f),
- hasActions (false),
- readActions (false),
- midiIn (true),
- midiInKeyPress (0x0),
- midiInKeyRel (0x0),
- midiInKill (0x0),
- midiInArm (0x0),
- midiInVolume (0x0),
- midiInMute (0x0),
- midiInSolo (0x0),
- midiInFilter (-1),
- midiOutL (false),
- midiOutLplaying(0x0),
- midiOutLmute (0x0),
- midiOutLsolo (0x0)
+Channel::Channel(ChannelType type, ID id, ID columnId, Frame bufferSize, const conf::Conf& conf)
+: id (id)
+, state (std::make_unique<ChannelState>(id, bufferSize))
+, midiLighter(state.get())
+, m_type (type)
+, m_columnId (columnId)
{
- buffer.alloc(bufferSize, G_MAX_IO_CHANS);
+ switch (m_type) {
+ case ChannelType::SAMPLE:
+ samplePlayer.emplace(state.get());
+ audioReceiver.emplace(state.get(), conf);
+ sampleActionRecorder.emplace(state.get(), samplePlayer->state.get());
+ break;
+
+ case ChannelType::PREVIEW:
+ samplePlayer.emplace(state.get());
+ break;
+
+ case ChannelType::MIDI:
+ midiController.emplace(state.get());
#ifdef WITH_VST
-
- midiBuffer.ensureSize(bufferSize);
-
+ midiReceiver.emplace(state.get());
#endif
+ midiSender.emplace(state.get());
+ midiActionRecorder.emplace(state.get());
+ break;
+
+ default: break;
+ }
}
Channel::Channel(const Channel& o)
-: type (o.type),
- playStatus (o.playStatus),
- recStatus (o.recStatus),
- columnId (o.columnId),
- id (o.id),
- height (o.height),
- previewMode (o.previewMode),
- pan (o.pan),
- volume (o.volume),
- armed (o.armed),
- name (o.name),
- key (o.key),
- mute (o.mute),
- solo (o.solo),
- volume_i (o.volume_i),
- volume_d (o.volume_d),
- hasActions (o.hasActions),
- readActions (o.readActions),
- midiIn (o.midiIn),
- midiInKeyPress (o.midiInKeyPress),
- midiInKeyRel (o.midiInKeyRel),
- midiInKill (o.midiInKill),
- midiInArm (o.midiInArm),
- midiInVolume (o.midiInVolume),
- midiInMute (o.midiInMute),
- midiInSolo (o.midiInSolo),
- midiInFilter (o.midiInFilter),
- midiOutL (o.midiOutL),
- midiOutLplaying(o.midiOutLplaying),
- midiOutLmute (o.midiOutLmute),
- midiOutLsolo (o.midiOutLsolo)
+: id (o.id)
#ifdef WITH_VST
- ,pluginIds (o.pluginIds)
+, pluginIds (o.pluginIds)
#endif
+, state (std::make_unique<ChannelState>(*o.state))
+, midiLearner (o.midiLearner)
+, midiLighter (o.midiLighter, state.get())
+, m_type (o.m_type)
+, m_columnId (o.m_columnId)
{
- buffer.alloc(o.buffer.countFrames(), G_MAX_IO_CHANS);
+ switch (m_type) {
+
+ case ChannelType::SAMPLE:
+ samplePlayer.emplace(o.samplePlayer.value(), state.get());
+ audioReceiver.emplace(o.audioReceiver.value(), state.get());
+ sampleActionRecorder.emplace(o.sampleActionRecorder.value(), state.get(), samplePlayer->state.get());
+ break;
+
+ case ChannelType::PREVIEW:
+ samplePlayer.emplace(o.samplePlayer.value(), state.get());
+ break;
+
+ case ChannelType::MIDI:
+ midiController.emplace(o.midiController.value(), state.get());
+#ifdef WITH_VST
+ midiReceiver.emplace(o.midiReceiver.value(), state.get());
+#endif
+ midiSender.emplace(o.midiSender.value(), state.get());
+ midiActionRecorder.emplace(o.midiActionRecorder.value(), state.get());
+ break;
+
+ default: break;
+ }
}
/* -------------------------------------------------------------------------- */
-Channel::Channel(const patch::Channel& p, int bufferSize)
-: type (p.type),
- playStatus (p.waveId == 0 && type == ChannelType::SAMPLE ? ChannelStatus::EMPTY : ChannelStatus::OFF),
- recStatus (ChannelStatus::OFF),
- columnId (p.columnId),
- id (p.id),
- height (p.height),
- previewMode (PreviewMode::NONE),
- pan (p.pan),
- volume (p.volume),
- armed (p.armed),
- name (p.name),
- key (p.key),
- mute (p.mute),
- solo (p.solo),
- volume_i (1.0),
- volume_d (0.0),
- hasActions (p.hasActions),
- readActions (p.readActions),
- midiIn (p.midiIn),
- midiInKeyPress (p.midiInKeyPress),
- midiInKeyRel (p.midiInKeyRel),
- midiInKill (p.midiInKill),
- midiInArm (p.midiInArm),
- midiInVolume (p.midiInVolume),
- midiInMute (p.midiInMute),
- midiInSolo (p.midiInSolo),
- midiInFilter (p.midiInFilter),
- midiOutL (p.midiOutL),
- midiOutLplaying(p.midiOutLplaying),
- midiOutLmute (p.midiOutLmute),
- midiOutLsolo (p.midiOutLsolo)
+Channel::Channel(const patch::Channel& p, Frame bufferSize)
+: id (p.id)
#ifdef WITH_VST
- ,pluginIds (p.pluginIds)
+, pluginIds (p.pluginIds)
#endif
+, state (std::make_unique<ChannelState>(p, bufferSize))
+, midiLearner (p)
+, midiLighter (p, state.get())
+, m_type (p.type)
+, m_columnId (p.columnId)
{
- buffer.alloc(bufferSize, G_MAX_IO_CHANS);
+ switch (m_type) {
+
+ case ChannelType::SAMPLE:
+ samplePlayer.emplace(p, state.get());
+ audioReceiver.emplace(p, state.get());
+ sampleActionRecorder.emplace(state.get(), samplePlayer->state.get());
+ break;
+
+ case ChannelType::PREVIEW:
+ samplePlayer.emplace(p, state.get());
+ break;
+
+ case ChannelType::MIDI:
+ midiController.emplace(state.get());
+#ifdef WITH_VST
+ midiReceiver.emplace(p, state.get());
+#endif
+ midiSender.emplace(p, state.get());
+ midiActionRecorder.emplace(state.get());
+ break;
+
+ default: break;
+ }
}
/* -------------------------------------------------------------------------- */
-bool Channel::isPlaying() const
+void Channel::parse(const mixer::EventBuffer& events, bool audible) const
{
- return playStatus == ChannelStatus::PLAY ||
- playStatus == ChannelStatus::ENDING;
+ for (const mixer::Event& e : events) {
+
+ if (e.action.channelId > 0 && e.action.channelId != id)
+ continue;
+
+ parse(e);
+ midiLighter.parse(e, audible);
+
+ if (midiController) midiController->parse(e);
+#ifdef WITH_VST
+ if (midiReceiver) midiReceiver->parse(e);
+#endif
+ if (midiSender) midiSender->parse(e);
+ if (samplePlayer) samplePlayer->parse(e);
+ if (midiActionRecorder) midiActionRecorder->parse(e);
+ if (sampleActionRecorder && samplePlayer && samplePlayer->hasWave())
+ sampleActionRecorder->parse(e);
+ }
}
/* -------------------------------------------------------------------------- */
-void Channel::sendMidiLmute()
+void Channel::advance(Frame bufferSize) const
{
- if (!midiOutL || midiOutLmute == 0x0)
- return;
- if (mute)
- kernelMidi::sendMidiLightning(midiOutLmute, midimap::midimap.muteOn);
- else
- kernelMidi::sendMidiLightning(midiOutLmute, midimap::midimap.muteOff);
+ /* TODO - this is used only to advance samplePlayer for its quantizer. Use
+ this to render actions in the future. */
+
+ if (samplePlayer) samplePlayer->advance(bufferSize);
}
/* -------------------------------------------------------------------------- */
-void Channel::sendMidiLsolo()
+void Channel::render(AudioBuffer* out, AudioBuffer* in, bool audible) const
{
- if (!midiOutL || midiOutLsolo == 0x0)
- return;
- if (solo)
- kernelMidi::sendMidiLightning(midiOutLsolo, midimap::midimap.soloOn);
+ if (id == mixer::MASTER_OUT_CHANNEL_ID)
+ renderMasterOut(*out);
else
- kernelMidi::sendMidiLightning(midiOutLsolo, midimap::midimap.soloOff);
+ if (id == mixer::MASTER_IN_CHANNEL_ID)
+ renderMasterIn(*in);
+ else
+ renderChannel(*out, *in, audible);
}
/* -------------------------------------------------------------------------- */
-void Channel::sendMidiLstatus()
+void Channel::parse(const mixer::Event& e) const
{
- if (!midiOutL || midiOutLplaying == 0x0)
- return;
- switch (playStatus) {
- case ChannelStatus::OFF:
- kernelMidi::sendMidiLightning(midiOutLplaying, midimap::midimap.stopped);
- break;
- case ChannelStatus::WAIT:
- kernelMidi::sendMidiLightning(midiOutLplaying, midimap::midimap.waiting);
- break;
- case ChannelStatus::ENDING:
- kernelMidi::sendMidiLightning(midiOutLplaying, midimap::midimap.stopping);
- break;
- case ChannelStatus::PLAY:
- if ((mixer::isChannelAudible(this) && !mute) ||
- !midimap::isDefined(midimap::midimap.playingInaudible))
- kernelMidi::sendMidiLightning(midiOutLplaying, midimap::midimap.playing);
- else
- kernelMidi::sendMidiLightning(midiOutLplaying, midimap::midimap.playingInaudible);
- break;
- default:
+ switch (e.type) {
+
+ case mixer::EventType::CHANNEL_VOLUME:
+ state->volume.store(e.action.event.getVelocityFloat()); break;
+
+ case mixer::EventType::CHANNEL_PAN:
+ state->pan.store(e.action.event.getVelocityFloat()); break;
+
+ case mixer::EventType::CHANNEL_MUTE:
+ state->mute.store(!state->mute.load()); break;
+
+ case mixer::EventType::CHANNEL_TOGGLE_ARM:
+ state->armed.store(!state->armed.load()); break;
+
+ case mixer::EventType::CHANNEL_SOLO:
+ state->solo.store(!state->solo.load());
+ m::mh::updateSoloCount();
break;
+
+ default: break;
}
}
/* -------------------------------------------------------------------------- */
-bool Channel::isMidiInAllowed(int c) const
+void Channel::renderMasterOut(AudioBuffer& out) const
{
- return midiInFilter == -1 || midiInFilter == c;
+ state->buffer.copyData(out);
+#ifdef WITH_VST
+ if (pluginIds.size() > 0)
+ pluginHost::processStack(state->buffer, pluginIds, nullptr);
+#endif
+ out.copyData(state->buffer, state->volume.load());
}
/* -------------------------------------------------------------------------- */
-void Channel::setPan(float v)
+void Channel::renderMasterIn(AudioBuffer& in) const
{
- if (v > 1.0f) v = 1.0f;
- else
- if (v < 0.0f) v = 0.0f;
- pan = v;
+#ifdef WITH_VST
+ if (pluginIds.size() > 0)
+ pluginHost::processStack(in, pluginIds, nullptr);
+#endif
}
-float Channel::getPan() const
+/* -------------------------------------------------------------------------- */
+
+
+void Channel::renderChannel(AudioBuffer& out, AudioBuffer& in, bool audible) const
{
- return pan;
-}
+ state->buffer.clear();
+ if (samplePlayer) samplePlayer->render(out);
+ if (audioReceiver) audioReceiver->render(in);
-/* -------------------------------------------------------------------------- */
+ /* If MidiReceiver exists, let it process the plug-in stack, as it can
+ contain plug-ins that take MIDI events (i.e. synths). Otherwise process the
+ plug-in stack internally with no MIDI events. */
+#ifdef WITH_VST
+ if (midiReceiver)
+ midiReceiver->render(pluginIds);
+ else
+ if (pluginIds.size() > 0)
+ pluginHost::processStack(state->buffer, pluginIds, nullptr);
+#endif
-float Channel::calcPanning(int ch) const
-{
- float p = pan;
- if (p == 0.5f) // center: nothing to do
- return 1.0;
- if (ch == 0)
- return 1.0 - p;
- else // channel 1
- return p;
+ if (audible)
+ out.addData(state->buffer, state->volume.load() * state->volume_i, calcPanning());
}
/* -------------------------------------------------------------------------- */
-void Channel::calcVolumeEnvelope()
+AudioBuffer::Pan Channel::calcPanning() const
{
- volume_i = volume_i + volume_d;
- if (volume_i < 0.0f)
- volume_i = 0.0f;
- else
- if (volume_i > 1.0f)
- volume_i = 1.0f;
-}
+ /* TODO - precompute the AudioBuffer::Pan when pan value changes instead of
+ building it on the fly. */
+
+ float pan = state->pan.load();
+ /* Center pan (0.5f)? Pass-through. */
-bool Channel::isPreview() const
-{
- return previewMode != PreviewMode::NONE;
+ if (pan == 0.5f) return { 1.0f, 1.0f };
+ return { 1.0f - pan, pan };
}
/* -------------------------------------------------------------------------- */
+ID Channel::getColumnId() const { return m_columnId; }
+ChannelType Channel::getType() const { return m_type; }
+
+
+/* -------------------------------------------------------------------------- */
+
+
bool Channel::isInternal() const
{
- return id == mixer::MASTER_OUT_CHANNEL_ID ||
- id == mixer::MASTER_IN_CHANNEL_ID ||
- id == mixer::PREVIEW_CHANNEL_ID;
+ return m_type == ChannelType::MASTER || m_type == ChannelType::PREVIEW;
}
-/* -------------------------------------------------------------------------- */
+bool Channel::isMuted() const
+{
+ /* Internals can't be muted. */
+ return !isInternal() && state->mute.load() == true;
+}
-bool Channel::isReadingActions() const
+bool Channel::canInputRec() const
{
- return hasActions && readActions;
+ if (m_type != ChannelType::SAMPLE)
+ return false;
+
+ bool armed = state->armed.load();
+ bool hasWave = samplePlayer->hasWave();
+ bool isProtected = audioReceiver->state->overdubProtection.load();
+ bool canOverdub = !hasWave || (hasWave && !isProtected);
+
+ return armed && canOverdub;
+}
+
+
+bool Channel::canActionRec() const
+{
+ return hasWave() && !samplePlayer->state->isAnyLoopMode();
}
+
+bool Channel::hasWave() const
+{
+ return m_type == ChannelType::SAMPLE && samplePlayer->hasWave();
+}
}} // giada::m::
#define G_CHANNEL_H
-#include <vector>
-#include <string>
-#include "core/types.h"
-#include "core/patch.h"
+#include <optional>
+#include "core/const.h"
#include "core/mixer.h"
-#include "core/midiMapConf.h"
-#include "core/midiEvent.h"
-#include "core/recorder.h"
-#include "core/audioBuffer.h"
+#include "core/channels/state.h"
+#include "core/channels/samplePlayer.h"
+#include "core/channels/audioReceiver.h"
#ifdef WITH_VST
-#include "deps/juce-config.h"
-#include "core/plugin.h"
-#include "core/pluginHost.h"
-#include "core/queue.h"
+#include "core/channels/midiReceiver.h"
#endif
+#include "core/channels/midiLearner.h"
+#include "core/channels/midiSender.h"
+#include "core/channels/midiController.h"
+#include "core/channels/midiLighter.h"
+#include "core/channels/sampleActionRecorder.h"
+#include "core/channels/midiActionRecorder.h"
namespace giada {
namespace m
{
-class Channel
+namespace conf
+{
+struct Conf;
+}
+class Channel final
{
public:
- virtual ~Channel() {};
-
- /* clone
- A trick to give the caller the ability to invoke the derived class copy
- constructor given the base. TODO - This thing will go away with the Channel
- "no-virtual inheritance" refactoring. */
-
- virtual Channel* clone() const = 0;
-
- /* load
- Loads persistence data into an existing channel. Used for built-in channels
- such as masters and preview. */
-
- virtual void load(const patch::Channel& p) {}
-
- /* parseEvents
- Prepares channel for rendering. This is called on each frame. */
-
- virtual void parseEvents(mixer::FrameEvents fe) {};
-
- /* render
- Audio rendering. Warning: inBuffer might be unallocated if no input devices
- are available for recording. */
-
- virtual void render(AudioBuffer& out, const AudioBuffer& in,
- AudioBuffer& inToOut, bool audible, bool running) {};
-
- /* start
- Action to do when channel starts. doQuantize = false (don't quantize)
- when Mixer is reading actions from Recorder. */
-
- virtual void start(int localFrame, bool doQuantize, int velocity) {};
-
- /* stop
- What to do when channel is stopped normally (via key or MIDI). */
-
- virtual void stop() {};
-
- /* kill
- What to do when channel stops abruptly. */
-
- virtual void kill(int localFrame) {};
-
- /* set mute
- What to do when channel is un/muted. */
-
- virtual void setMute(bool value) {};
-
- /* set solo
- What to do when channel is un/soloed. */
-
- virtual void setSolo(bool value) {};
-
- /* empty
- Frees any associated resources (e.g. waveform for SAMPLE). */
-
- virtual void empty() {};
-
- /* stopBySeq
- What to do when channel is stopped by sequencer. */
-
- virtual void stopBySeq(bool chansStopOnSeqHalt) {};
-
- /* rewind
- Rewinds channel when rewind button is pressed. */
-
- virtual void rewindBySeq() {};
-
- /* canInputRec
- Tells whether a channel can accept and handle input audio. Always false for
- Midi channels, true for Sample channels only if they don't contain a
- sample yet.*/
-
- virtual bool canInputRec() const { return false; };
- virtual bool hasLogicalData() const { return false; };
- virtual bool hasEditedData() const { return false; };
- virtual bool hasData() const { return false; };
+ Channel(ChannelType t, ID id, ID columnId, Frame bufferSize, const conf::Conf& c);
+ Channel(const Channel&);
+ Channel(const patch::Channel& p, Frame bufferSize);
+ Channel(Channel&&) = default;
+ Channel& operator=(const Channel&) = delete;
+ Channel& operator=(Channel&&) = delete;
+ ~Channel() = default;
- virtual bool recordStart(bool canQuantize) { return true; };
- virtual bool recordKill() { return true; };
- virtual void recordStop() {};
+ /* parse
+ Parses live events. */
- virtual void startReadingActions(bool treatRecsAsLoops,
- bool recsStopOnChanHalt) {};
- virtual void stopReadingActions(bool running, bool treatRecsAsLoops,
- bool recsStopOnChanHalt) {};
+ void parse(const mixer::EventBuffer& e, bool audible) const;
- virtual void stopInputRec(int globalFrame) {};
+ /* advance
+ Processes static events (e.g. actions) in the current block. */
- /* receiveMidi
- Receives and processes midi messages from external devices. */
+ void advance(Frame bufferSize) const;
- virtual void receiveMidi(const MidiEvent& midiEvent) {};
+ /* render
+ Renders audio data to I/O buffers. */
+
+ void render(AudioBuffer* out, AudioBuffer* in, bool audible) const;
- /* calcPanning
- Given an audio channel (stereo: 0 or 1) computes the current panning value. */
+ bool isInternal() const;
+ bool isMuted() const;
+ bool canInputRec() const;
+ bool canActionRec() const;
+ bool hasWave() const;
+ ID getColumnId() const;
+ ChannelType getType() const;
+
+ ID id;
- float calcPanning(int ch) const;
-
- bool isPlaying() const;
- float getPan() const;
- bool isPreview() const;
- bool isInternal() const;
-
- /* isMidiInAllowed
- Given a MIDI channel 'c' tells whether this channel should be allowed to
- receive and process MIDI events on MIDI channel 'c'. */
-
- bool isMidiInAllowed(int c) const;
-
- /* isReadingActions
- Tells whether the channel as actions and it is currently reading them. */
-
- bool isReadingActions() const;
-
- /* sendMidiL*
- Sends MIDI lightning events to a physical device. */
-
- void sendMidiLmute();
- void sendMidiLsolo();
- void sendMidiLstatus();
-
- void setPan(float v);
-
- void calcVolumeEnvelope();
-
- /* buffer
- Working buffer for internal processing. */
-
- AudioBuffer buffer;
-
- ChannelType type;
- ChannelStatus playStatus;
- ChannelStatus recStatus;
-
- ID columnId;
- ID id;
-
- int height;
-
- /* previewMode
- Whether the channel is in audio preview mode or not. */
-
- PreviewMode previewMode;
-
- float pan;
- float volume; // global volume
- bool armed;
- std::string name;
- int key;
- bool mute;
- bool solo;
-
- /* volume_*
- Internal volume variables: volume_i for envelopes, volume_d keeps track of
- the delta during volume changes (or the line slope between two volume
- points). */
-
- double volume_i;
- double volume_d;
-
- bool hasActions; // If has some actions recorded
- bool readActions; // If should read recorded actions
-
- bool midiIn; // enable midi input
- uint32_t midiInKeyPress;
- uint32_t midiInKeyRel;
- uint32_t midiInKill;
- uint32_t midiInArm;
- uint32_t midiInVolume;
- uint32_t midiInMute;
- uint32_t midiInSolo;
-
- /* midiInFilter
- Which MIDI channel should be filtered out when receiving MIDI messages. -1
- means 'all'. */
+#ifdef WITH_VST
+ std::vector<ID> pluginIds;
+#endif
- int midiInFilter;
+ /* state
+ Pointer to mutable Channel state. */
- /* midiOutL*
- Enables MIDI lightning output, plus a set of midi lighting event to be sent
- to a device. Those events basically contains the MIDI channel, everything
- else gets stripped out. */
+ std::unique_ptr<ChannelState> state;
- bool midiOutL;
- uint32_t midiOutLplaying;
- uint32_t midiOutLmute;
- uint32_t midiOutLsolo;
+ MidiLearner midiLearner;
+ MidiLighter midiLighter;
+ std::optional<SamplePlayer> samplePlayer;
+ std::optional<AudioReceiver> audioReceiver;
+ std::optional<MidiController> midiController;
#ifdef WITH_VST
+ std::optional<MidiReceiver> midiReceiver;
+#endif
+ std::optional<MidiSender> midiSender;
+ std::optional<SampleActionRecorder> sampleActionRecorder;
+ std::optional<MidiActionRecorder> midiActionRecorder;
- std::vector<ID> pluginIds;
-
- /* MidiBuffer
- Contains MIDI events. When ready, events are sent to each plugin in the
- channel. This is available for any kind of channel, but it makes sense only
- for MIDI channels. */
-
- juce::MidiBuffer midiBuffer;
-
- /* midiQueue
- FIFO queue for collecting MIDI events from the MIDI thread and passing them
- to the audio thread. */
- /* TODO - magic number */
+private:
- Queue<MidiEvent, 32> midiQueue;
+ void parse(const mixer::Event& e) const;
-#endif
+ void renderMasterOut(AudioBuffer& out) const;
+ void renderMasterIn(AudioBuffer& in) const;
+ void renderChannel(AudioBuffer& out, AudioBuffer& in, bool audible) const;
-protected:
+ AudioBuffer::Pan calcPanning() const;
- Channel(ChannelType type, ChannelStatus status, int bufferSize,
- ID columnId, ID id);
- Channel(const Channel& o);
- Channel(const patch::Channel& p, int bufferSize);
+ ChannelType m_type;
+ ID m_columnId;
};
-
}} // giada::m::
#include <cassert>
#include "utils/fs.h"
-#include "core/channels/sampleChannel.h"
-#include "core/channels/midiChannel.h"
-#include "core/channels/masterChannel.h"
#include "core/channels/channel.h"
+#include "core/channels/samplePlayer.h"
#include "core/const.h"
+#include "core/kernelAudio.h"
#include "core/patch.h"
#include "core/mixer.h"
#include "core/idManager.h"
#include "core/wave.h"
#include "core/waveManager.h"
-#include "core/pluginHost.h"
-#include "core/pluginManager.h"
-#include "core/plugin.h"
+#include "core/plugins/pluginHost.h"
+#include "core/plugins/pluginManager.h"
+#include "core/plugins/plugin.h"
#include "core/action.h"
#include "core/recorderHandler.h"
#include "channelManager.h"
/* -------------------------------------------------------------------------- */
-std::unique_ptr<Channel> create(ChannelType type, int bufferSize,
- bool inputMonitorOn, ID columnId)
+std::unique_ptr<Channel> create(ChannelType type, ID columnId, const conf::Conf& conf)
{
- std::unique_ptr<Channel> ch = nullptr;
-
- if (type == ChannelType::SAMPLE)
- ch = std::make_unique<SampleChannel>(inputMonitorOn, bufferSize, columnId, channelId_.get());
- else
- if (type == ChannelType::MIDI)
- ch = std::make_unique<MidiChannel>(bufferSize, columnId, channelId_.get());
- else
- if (type == ChannelType::MASTER)
- ch = std::make_unique<MasterChannel>(bufferSize, channelId_.get());
- else
- if (type == ChannelType::PREVIEW)
- ch = std::make_unique<MasterChannel>(bufferSize, channelId_.get()); // TODO - temporary placeholder
+ std::unique_ptr<Channel> ch = std::make_unique<Channel>(type,
+ channelId_.get(), columnId, kernelAudio::getRealBufSize(), conf);
- assert(ch != nullptr);
return ch;
}
std::unique_ptr<Channel> create(const Channel& o)
{
- std::unique_ptr<Channel> ch = nullptr;
-
- if (o.type == ChannelType::SAMPLE)
- ch = std::make_unique<SampleChannel>(static_cast<const SampleChannel&>(o));
- else
- if (o.type == ChannelType::MIDI)
- ch = std::make_unique<MidiChannel>(static_cast<const MidiChannel&>(o));
- else
- if (o.type == ChannelType::MASTER)
- ch = std::make_unique<MasterChannel>(static_cast<const MasterChannel&>(o));
-
- assert(ch != nullptr);
-
- if (o.type != ChannelType::MASTER)
- ch->id = channelId_.get();
-
+ std::unique_ptr<Channel> ch = std::make_unique<Channel>(o);
+ ID id = channelId_.get();
+ ch->id = id;
+ ch->state->id = id;
return ch;
}
std::unique_ptr<Channel> deserializeChannel(const patch::Channel& pch, int bufferSize)
{
- std::unique_ptr<Channel> ch = nullptr;
-
- if (pch.type == ChannelType::SAMPLE)
- ch = std::make_unique<SampleChannel>(pch, bufferSize);
- else
- if (pch.type == ChannelType::MIDI)
- ch = std::make_unique<MidiChannel>(pch, bufferSize);
-
- assert(ch != nullptr);
-
channelId_.set(pch.id);
-
- return ch;
+ return std::make_unique<Channel>(pch, bufferSize);
}
{
patch::Channel pc;
- pc.id = c.id;
- pc.type = c.type;
-
#ifdef WITH_VST
for (ID pid : c.pluginIds)
pc.pluginIds.push_back(pid);
-#endif
-
- if (c.type != ChannelType::MASTER) {
- pc.height = c.height;
- pc.name = c.name.c_str();
- pc.columnId = c.columnId;
- pc.key = c.key;
- pc.mute = c.mute;
- pc.solo = c.solo;
- pc.volume = c.volume;
- pc.pan = c.pan;
- pc.hasActions = c.hasActions;
- pc.armed = c.armed;
- pc.midiIn = c.midiIn;
- pc.midiInKeyPress = c.midiInKeyRel;
- pc.midiInKeyRel = c.midiInKeyPress;
- pc.midiInKill = c.midiInKill;
- pc.midiInArm = c.midiInArm;
- pc.midiInVolume = c.midiInVolume;
- pc.midiInMute = c.midiInMute;
- pc.midiInSolo = c.midiInSolo;
- pc.midiInFilter = c.midiInFilter;
- pc.midiOutL = c.midiOutL;
- pc.midiOutLplaying = c.midiOutLplaying;
- pc.midiOutLmute = c.midiOutLmute;
- pc.midiOutLsolo = c.midiOutLsolo;
- }
+#endif
+
+ pc.id = c.id;
+ pc.type = c.getType();
+ pc.columnId = c.getColumnId();
+ pc.height = c.state->height;
+ pc.name = c.state->name;
+ pc.key = c.state->key.load();
+ pc.mute = c.state->mute.load();
+ pc.solo = c.state->solo.load();
+ pc.volume = c.state->volume.load();
+ pc.pan = c.state->pan.load();
+ pc.hasActions = c.state->hasActions;
+ pc.readActions = c.state->readActions.load();
+ pc.armed = c.state->armed.load();
+ pc.midiIn = c.midiLearner.state->enabled.load();
+ pc.midiInFilter = c.midiLearner.state->filter.load();
+ pc.midiInKeyPress = c.midiLearner.state->keyPress.getValue();
+ pc.midiInKeyRel = c.midiLearner.state->keyRelease.getValue();
+ pc.midiInKill = c.midiLearner.state->kill.getValue();
+ pc.midiInArm = c.midiLearner.state->arm.getValue();
+ pc.midiInVolume = c.midiLearner.state->volume.getValue();
+ pc.midiInMute = c.midiLearner.state->mute.getValue();
+ pc.midiInSolo = c.midiLearner.state->solo.getValue();
+ pc.midiInReadActions = c.midiLearner.state->readActions.getValue();
+ pc.midiInPitch = c.midiLearner.state->pitch.getValue();
+ pc.midiOutL = c.midiLighter.state->enabled.load();
+ pc.midiOutLplaying = c.midiLighter.state->playing.getValue();
+ pc.midiOutLmute = c.midiLighter.state->mute.getValue();
+ pc.midiOutLsolo = c.midiLighter.state->solo.getValue();
+
+ if (c.getType() == ChannelType::SAMPLE) {
+ pc.waveId = c.samplePlayer->getWaveId();
+ pc.mode = c.samplePlayer->state->mode.load();
+ pc.begin = c.samplePlayer->state->begin.load();
+ pc.end = c.samplePlayer->state->end.load();
+ pc.pitch = c.samplePlayer->state->pitch.load();
+ pc.shift = c.samplePlayer->state->shift.load();
+ pc.midiInVeloAsVol = c.samplePlayer->state->velocityAsVol.load();
+ pc.inputMonitor = c.audioReceiver->state->inputMonitor.load();
+ pc.overdubProtection = c.audioReceiver->state->overdubProtection.load();
- if (c.type == ChannelType::SAMPLE) {
- const SampleChannel& sc = static_cast<const SampleChannel&>(c);
- pc.waveId = sc.waveId;
- pc.mode = sc.mode;
- pc.begin = sc.begin;
- pc.end = sc.end;
- pc.readActions = sc.readActions;
- pc.pitch = sc.pitch;
- pc.inputMonitor = sc.inputMonitor;
- pc.midiInVeloAsVol = sc.midiInVeloAsVol;
- pc.midiInReadActions = sc.midiInReadActions;
- pc.midiInPitch = sc.midiInPitch;
}
else
- if (c.type == ChannelType::MIDI) {
- const MidiChannel& mc = static_cast<const MidiChannel&>(c);
- pc.midiOut = mc.midiOut;
- pc.midiOutChan = mc.midiOutChan;
+ if (c.getType() == ChannelType::MIDI) {
+ pc.midiOut = c.midiSender->state->enabled.load();
+ pc.midiOutChan = c.midiSender->state->filter.load();
}
return pc;
}
-}}}; // giada::m::channelManager
+}}} // giada::m::channelManager
namespace giada {
namespace m
{
+namespace conf
+{
+struct Conf;
+}
namespace patch
{
struct Channel;
}
-class Channel;
-class SampleChannel;
-class MidiChannel;
+class Channel;
+struct ChannelState;
namespace channelManager
{
/* init
/* create (1)
Creates a new Channel from scratch. */
-std::unique_ptr<Channel> create(ChannelType type, int bufferSize,
- bool inputMonitorOn, ID columnId);
+std::unique_ptr<Channel> create(ChannelType type, ID columnId, const conf::Conf& conf);
/* create (2)
Creates a new Channel given an existing one (i.e. clone). */
std::unique_ptr<Channel> deserializeChannel(const patch::Channel& c, int bufferSize);
const patch::Channel serializeChannel(const Channel& c);
-}}}; // giada::m::channelManager
+}}} // giada::m::channelManager
#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include "masterChannel.h"
-
-
-namespace giada {
-namespace m
-{
-MasterChannel::MasterChannel(int bufferSize, ID id)
-: Channel(ChannelType::MASTER, ChannelStatus::OFF, bufferSize, 0, id)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-MasterChannel::MasterChannel(const patch::Channel& p, int bufferSize)
-: Channel(p, bufferSize)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-MasterChannel* MasterChannel::clone() const
-{
- return new MasterChannel(*this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MasterChannel::load(const patch::Channel& p)
-{
- volume = p.volume;
-#ifdef WITH_VST
- pluginIds = p.pluginIds;
-#endif
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MasterChannel::render(AudioBuffer& out, const AudioBuffer& in,
- AudioBuffer& inToOut, bool audible, bool running)
-{
-#ifdef WITH_VST
- if (pluginIds.size() == 0)
- return;
- if (id == mixer::MASTER_OUT_CHANNEL_ID)
- pluginHost::processStack(out, pluginIds);
- else
- if (id == mixer::MASTER_IN_CHANNEL_ID)
- pluginHost::processStack(inToOut, pluginIds);
-#endif
-}
-
-}} // giada::m::
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef G_MASTER_CHANNEL_H
-#define G_MASTER_CHANNEL_H
-
-
-#include "core/channels/channel.h"
-
-
-namespace giada {
-namespace m
-{
-class MasterChannel : public Channel
-{
-public:
-
- MasterChannel(int bufferSize, ID id);
- MasterChannel(const patch::Channel& p, int bufferSize);
-
- MasterChannel* clone() const override;
- void load(const patch::Channel& p) override;
- void parseEvents(mixer::FrameEvents fe) override {};
- void render(AudioBuffer& out, const AudioBuffer& in, AudioBuffer& inToOut,
- bool audible, bool running) override;
- void start(int frame, bool doQuantize, int velocity) override {};
- void kill(int localFrame) override {};
- void empty() override {};
- void stopBySeq(bool chansStopOnSeqHalt) override {};
- void stop() override {};
- void rewindBySeq() override {};
- void setMute(bool value) override {};
- void setSolo(bool value) override {};
- void receiveMidi(const MidiEvent& midiEvent) override {};
-};
-
-}} // giada::m::
-
-
-#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cassert>
+#include "core/action.h"
+#include "core/clock.h"
+#include "core/conf.h"
+#include "core/mixer.h"
+#include "core/recorderHandler.h"
+#include "core/recManager.h"
+#include "core/channels/state.h"
+#include "midiActionRecorder.h"
+
+
+namespace giada {
+namespace m
+{
+MidiActionRecorder::MidiActionRecorder(ChannelState* c)
+: m_channelState(c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MidiActionRecorder::MidiActionRecorder(const MidiActionRecorder& /*o*/, ChannelState* c)
+: MidiActionRecorder(c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiActionRecorder::parse(const mixer::Event& e) const
+{
+ assert(m_channelState != nullptr);
+
+ if (e.type == mixer::EventType::MIDI && canRecord())
+ record(e.action.event);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiActionRecorder::record(const MidiEvent& e) const
+{
+ MidiEvent flat(e);
+ flat.setChannel(0);
+ recorderHandler::liveRec(m_channelState->id, flat, clock::quantize(clock::getCurrentFrame()));
+ m_channelState->hasActions = true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool MidiActionRecorder::canRecord() const
+{
+ return recManager::isRecordingAction() &&
+ clock::isRunning() &&
+ !recManager::isRecordingInput();
+}
+}} // giada::m::
+
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_CHANNEL_MIDI_ACTION_RECORDER_H
+#define G_CHANNEL_MIDI_ACTION_RECORDER_H
+
+
+#include "core/types.h"
+
+
+namespace giada {
+namespace m
+{
+namespace mixer
+{
+struct Event;
+}
+struct ChannelState;
+class MidiActionRecorder
+{
+public:
+
+ MidiActionRecorder(ChannelState*);
+ MidiActionRecorder(const MidiActionRecorder&, ChannelState* c=nullptr);
+
+ void parse(const mixer::Event& e) const;
+
+private:
+
+ bool canRecord() const;
+ void record(const MidiEvent& e) const;
+
+ ChannelState* m_channelState;
+};
+}} // giada::m::
+
+
+#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <cassert>
-#include "utils/log.h"
-#include "core/channels/midiChannelProc.h"
-#include "core/channels/channelManager.h"
-#include "core/channels/channel.h"
-#include "core/recorder.h"
-#include "core/recorderHandler.h"
-#include "core/recManager.h"
-#include "core/action.h"
-#include "core/patch.h"
-#include "core/const.h"
-#include "core/conf.h"
-#include "core/mixer.h"
-#include "core/pluginHost.h"
-#include "core/kernelMidi.h"
-#include "midiChannel.h"
-
-
-namespace giada {
-namespace m
-{
-MidiChannel::MidiChannel(int bufferSize, ID columnId, ID id)
-: Channel (ChannelType::MIDI, ChannelStatus::OFF, bufferSize, columnId, id),
- midiOut (false),
- midiOutChan(G_MIDI_CHANS[0])
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-MidiChannel::MidiChannel(const MidiChannel& o)
-: Channel (o),
- midiOut (o.midiOut),
- midiOutChan(o.midiOutChan)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-MidiChannel::MidiChannel(const patch::Channel& p, int bufferSize)
-: Channel (p, bufferSize),
- midiOut (p.midiOut),
- midiOutChan(p.midiOutChan)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-MidiChannel* MidiChannel::clone() const
-{
- return new MidiChannel(*this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::parseEvents(mixer::FrameEvents fe)
-{
- midiChannelProc::parseEvents(this, fe);
-}
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::render(AudioBuffer& out, const AudioBuffer& in,
- AudioBuffer& inToOut, bool audible, bool running)
-{
- midiChannelProc::process(this, out, in, audible);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::stopBySeq(bool chansStopOnSeqHalt)
-{
- midiChannelProc::stopBySeq(this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::start(int frame, bool doQuantize, int velocity)
-{
- midiChannelProc::start(this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::kill(int localFrame)
-{
- midiChannelProc::kill(this, localFrame);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::rewindBySeq()
-{
- midiChannelProc::rewindBySeq(this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::setMute(bool value)
-{
- midiChannelProc::setMute(this, value);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::setSolo(bool value)
-{
- midiChannelProc::setSolo(this, value);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::empty()
-{
- hasActions = false;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::sendMidi(const MidiEvent& e, int localFrame)
-{
- if (midiOut) {
- MidiEvent e_ = e;
- e_.setChannel(midiOutChan);
- kernelMidi::send(e_.getRaw());
- }
-
-#ifdef WITH_VST
-
- /* Enqueue this MIDI event for plug-ins processing. Will be read and
- rendered later on by the audio thread. */
-
- MidiEvent e_ = e;
- e_.setDelta(localFrame);
- midiQueue.push(e_);
-
-#endif
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void MidiChannel::receiveMidi(const MidiEvent& midiEvent)
-{
- namespace mrh = m::recorderHandler;
- namespace mr = m::recorder;
-
- if (!armed)
- return;
-
- /* 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. */
-
- MidiEvent midiEventFlat(midiEvent);
- midiEventFlat.setChannel(0);
-
-#ifdef WITH_VST
-
- /* Enqueue this MIDI event for plug-ins processing. Will be read and
- rendered later on by the audio thread. */
-
- midiQueue.push(midiEventFlat);
-
-#endif
-
- if (recManager::isRecordingAction()) {
- mrh::liveRec(id, midiEventFlat);
- hasActions = true;
- }
-}
-
-}} // giada::m::
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef G_MIDI_CHANNEL_H
-#define G_MIDI_CHANNEL_H
-
-
-#ifdef WITH_VST
-#include "deps/juce-config.h"
-#endif
-#include "core/channels/channel.h"
-
-
-namespace giada {
-namespace m
-{
-class MidiChannel : public Channel
-{
-public:
-
- MidiChannel(int bufferSize, ID columnId, ID id);
- MidiChannel(const MidiChannel& o);
- MidiChannel(const patch::Channel& p, int bufferSize);
-
- MidiChannel* clone() const override;
- void parseEvents(mixer::FrameEvents fe) override;
- void render(AudioBuffer& out, const AudioBuffer& in, AudioBuffer& inToOut,
- bool audible, bool running) override;
- void start(int frame, bool doQuantize, int velocity) override;
- void kill(int localFrame) override;
- void empty() override;
- void stopBySeq(bool chansStopOnSeqHalt) override;
- void stop() override {};
- void rewindBySeq() override;
- void setMute(bool value) override;
- void setSolo(bool value) override;
- void receiveMidi(const MidiEvent& midiEvent) override;
-
- /* sendMidi
- Sends Midi event to the outside world. */
-
- void sendMidi(const MidiEvent& e, int localFrame);
-
- bool midiOut; // enable midi output
- int midiOutChan; // midi output channel
-};
-
-}} // giada::m::
-
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <cassert>
-#include "core/channels/midiChannel.h"
-#include "core/model/model.h"
-#include "core/pluginHost.h"
-#include "core/kernelMidi.h"
-#include "core/const.h"
-#include "core/action.h"
-#include "core/mixerHandler.h"
-#include "midiChannelProc.h"
-
-
-namespace giada {
-namespace m {
-namespace midiChannelProc
-{
-namespace
-{
-void onFirstBeat_(MidiChannel* ch)
-{
- if (ch->playStatus == ChannelStatus::ENDING) {
- ch->playStatus = ChannelStatus::OFF;
- ch->sendMidiLstatus();
- }
- else
- if (ch->playStatus == ChannelStatus::WAIT) {
- ch->playStatus = ChannelStatus::PLAY;
- ch->sendMidiLstatus();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void sendAllNotesOff_(MidiChannel* ch)
-{
- MidiEvent e(MIDI_ALL_NOTES_OFF);
- ch->sendMidi(e, /*localFrame=*/0);
-
-}
-}; // {anonymous}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-void parseEvents(MidiChannel* ch, mixer::FrameEvents fe)
-{
- if (fe.onFirstBeat)
- onFirstBeat_(ch);
- if (fe.actions != nullptr)
- for (const Action& action : *fe.actions)
- if (action.channelId == ch->id && ch->isPlaying() && !ch->mute)
- ch->sendMidi(action.event, fe.frameLocal);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void process(MidiChannel* ch, AudioBuffer& out, const AudioBuffer& in, bool audible)
-{
-#ifdef WITH_VST
-
- ch->midiBuffer.clear();
-
- /* Fill the MIDI buffer vector with messages coming from the MIDI queue
- filled by the MIDI thread. This is for live events, e.g. piano keyboards,
- controllers, ... */
-
- MidiEvent e;
- while (ch->midiQueue.pop(e)) {
- juce::MidiMessage message = juce::MidiMessage(
- e.getStatus(),
- e.getNote(),
- e.getVelocity());
- ch->midiBuffer.addEvent(message, e.getDelta());
- }
- pluginHost::processStack(ch->buffer, ch->pluginIds, &ch->midiBuffer);
-
- /* Process the plugin stack first, then quit if the channel is muted/soloed.
- This way there's no risk of cutting midi event pairs such as note-on and
- note-off while triggering a mute/solo. */
-
- if (!audible)
- return;
-
- for (int i=0; i<out.countFrames(); i++)
- for (int j=0; j<out.countChannels(); j++)
- out[i][j] += ch->buffer[i][j] * ch->volume;
-
-#endif
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void start(MidiChannel* ch)
-{
- switch (ch->playStatus) {
- case ChannelStatus::PLAY:
- ch->playStatus = ChannelStatus::ENDING;
- ch->sendMidiLstatus();
- break;
-
- case ChannelStatus::ENDING:
- case ChannelStatus::WAIT:
- ch->playStatus = ChannelStatus::OFF;
- ch->sendMidiLstatus();
- break;
-
- case ChannelStatus::OFF:
- ch->playStatus = ChannelStatus::WAIT;
- ch->sendMidiLstatus();
- break;
-
- default: break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void kill(MidiChannel* ch, int localFrame)
-{
- if (ch->isPlaying())
- sendAllNotesOff_(ch);
-
- ch->playStatus = ChannelStatus::OFF;
- ch->sendMidiLstatus();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void rewindBySeq(MidiChannel* ch)
-{
- sendAllNotesOff_(ch);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setMute(MidiChannel* ch, bool v)
-{
- ch->mute = v;
- if (v)
- sendAllNotesOff_(ch);
-
- // This is for processing playing_inaudible
- ch->sendMidiLstatus();
-
- ch->sendMidiLmute();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setSolo(MidiChannel* ch, bool v)
-{
- ch->solo = v;
- mh::updateSoloCount();
-
- // This is for processing playing_inaudible
- // TODO
- //for (std::unique_ptr<Channel>& c : model::getLayout()->channels)
- // c->sendMidiLstatus();
-
- ch->sendMidiLsolo();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void stopBySeq(MidiChannel* ch)
-{
- sendAllNotesOff_(ch);
- kill(ch, 0);
-}
-}}};
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef G_MIDI_CHANNEL_PROC_H
-#define G_MIDI_CHANNEL_PROC_H
-
-
-#include "core/mixer.h"
-#include "core/audioBuffer.h"
-
-
-namespace giada {
-namespace m
-{
-class MidiChannel;
-namespace midiChannelProc
-{
-/* parseEvents
-Parses events gathered by Mixer::masterPlay(). */
-
-void parseEvents(MidiChannel* ch, mixer::FrameEvents ev);
-
-/**/
-void process(MidiChannel* ch, AudioBuffer& out, const AudioBuffer& in, bool audible);
-
-/* kill
-Stops a channel abruptly. */
-
-void kill(MidiChannel* ch, int localFrame);
-
-/* start
-Starts a channel. */
-
-void start(MidiChannel* ch);
-
-/* stopBySeq
-Stops a channel when the stop button on main transport is pressed. */
-
-void stopBySeq(MidiChannel* ch);
-
-/* rewind
-Rewinds channel when rewind button on main transport is pressed. */
-
-void rewindBySeq(MidiChannel* ch);
-
-/* mute|unmute
-Mutes/unmutes a channel. */
-
-void setMute(MidiChannel* ch, bool v);
-void setSolo(MidiChannel* ch, bool v);
-}}};
-
-
-#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cassert>
+#include "core/conf.h"
+#include "core/channels/state.h"
+#include "midiController.h"
+
+
+namespace giada {
+namespace m
+{
+MidiController::MidiController(ChannelState* c)
+: m_channelState(c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MidiController::MidiController(const MidiController& /*o*/, ChannelState* c)
+: m_channelState(c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiController::parse(const mixer::Event& e) const
+{
+ assert(m_channelState != nullptr);
+
+ switch (e.type) {
+
+ case mixer::EventType::KEY_PRESS:
+ press(); break;
+
+ case mixer::EventType::KEY_KILL:
+ case mixer::EventType::SEQUENCER_STOP:
+ kill(); break;
+
+ case mixer::EventType::SEQUENCER_FIRST_BEAT:
+ case mixer::EventType::SEQUENCER_REWIND:
+ onFirstBeat();
+
+ default: break;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiController::press() const
+{
+ ChannelStatus playStatus = m_channelState->playStatus.load();
+
+ 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;
+ }
+
+ m_channelState->playStatus.store(playStatus);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiController::kill() const
+{
+ m_channelState->playStatus.store(ChannelStatus::OFF);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiController::onFirstBeat() const
+{
+ ChannelStatus playStatus = m_channelState->playStatus.load();
+
+ if (playStatus == ChannelStatus::ENDING)
+ playStatus = ChannelStatus::OFF;
+ else
+ if (playStatus == ChannelStatus::WAIT)
+ playStatus = ChannelStatus::PLAY;
+
+ m_channelState->playStatus.store(playStatus);
+}
+}} // giada::m::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_CHANNEL_MIDI_CONTROLLER_H
+#define G_CHANNEL_MIDI_CONTROLLER_H
+
+
+#include "core/types.h"
+#include "core/mixer.h" // TODO - forward declare
+
+
+namespace giada {
+namespace m
+{
+/* MidiController
+Manages events for a MIDI Channel. */
+
+class MidiController
+{
+public:
+
+ MidiController(ChannelState*);
+ MidiController(const MidiController&, ChannelState* c=nullptr);
+
+ void parse(const mixer::Event& e) const;
+
+private:
+
+ void press() const;
+ void kill() const;
+ void onFirstBeat() const;
+
+ ChannelState* m_channelState;
+};
+}} // giada::m::
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "core/channels/state.h"
+#include "midiLearner.h"
+
+
+namespace giada {
+namespace m
+{
+MidiLearner::MidiLearner()
+: state(std::make_unique<MidiLearnerState>())
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MidiLearner::MidiLearner(const patch::Channel& p)
+: state(std::make_unique<MidiLearnerState>(p))
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MidiLearner::MidiLearner(const MidiLearner& o)
+: state(std::make_unique<MidiLearnerState>(*o.state))
+{
+}
+}} // giada::m::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_CHANNEL_MIDI_LEARNER_H
+#define G_CHANNEL_MIDI_LEARNER_H
+
+
+#include <memory>
+
+
+namespace giada {
+namespace m
+{
+struct MidiLearnerState;
+class MidiLearner
+{
+public:
+
+ MidiLearner();
+ MidiLearner(const patch::Channel&);
+ MidiLearner(const MidiLearner&);
+
+ /* state
+ Pointer to mutable MidiLearnerState state. */
+
+ std::unique_ptr<MidiLearnerState> state;
+};
+}} // giada::m::
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "core/channels/state.h"
+#include "core/mixer.h"
+#include "core/kernelMidi.h"
+#include "core/midiMapConf.h"
+#include "midiLighter.h"
+
+
+namespace giada {
+namespace m
+{
+MidiLighter::MidiLighter(ChannelState* c)
+: state (std::make_unique<MidiLighterState>())
+, m_channelState(c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MidiLighter::MidiLighter(const patch::Channel& p, ChannelState* c)
+: state (std::make_unique<MidiLighterState>(p))
+, m_channelState(c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MidiLighter::MidiLighter(const MidiLighter& o, ChannelState* c)
+: state (std::make_unique<MidiLighterState>(*o.state))
+, m_channelState(c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiLighter::parse(const mixer::Event& e, bool audible) const
+{
+ if (state->enabled.load() == false)
+ return;
+
+ uint32_t l_playing = state->playing.getValue();
+ uint32_t l_mute = state->mute.getValue();
+ uint32_t l_solo = state->solo.getValue();
+
+ switch (e.type) {
+
+ case mixer::EventType::KEY_PRESS:
+ case mixer::EventType::KEY_RELEASE:
+ case mixer::EventType::KEY_KILL:
+ case mixer::EventType::SEQUENCER_STOP:
+ if (l_playing != 0x0) sendStatus(l_playing, audible);
+ break;
+
+ case mixer::EventType::CHANNEL_MUTE:
+ if (l_mute != 0x0) sendMute(l_mute);
+ break;
+
+ case mixer::EventType::CHANNEL_SOLO:
+ if (l_solo != 0x0) sendSolo(l_solo);
+ break;
+
+ default: break;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiLighter::sendMute(uint32_t l_mute) const
+{
+ if (m_channelState->mute.load() == true)
+ kernelMidi::sendMidiLightning(l_mute, midimap::midimap.muteOn);
+ else
+ kernelMidi::sendMidiLightning(l_mute, midimap::midimap.muteOff);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiLighter::sendSolo(uint32_t l_solo) const
+{
+ if (m_channelState->solo.load() == true)
+ kernelMidi::sendMidiLightning(l_solo, midimap::midimap.soloOn);
+ else
+ kernelMidi::sendMidiLightning(l_solo, midimap::midimap.soloOff);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiLighter::sendStatus(uint32_t l_playing, bool audible) const
+{
+ switch (m_channelState->playStatus.load()) {
+
+ case ChannelStatus::OFF:
+ kernelMidi::sendMidiLightning(l_playing, midimap::midimap.stopped);
+ break;
+
+ case ChannelStatus::WAIT:
+ kernelMidi::sendMidiLightning(l_playing, midimap::midimap.waiting);
+ break;
+
+ case ChannelStatus::ENDING:
+ kernelMidi::sendMidiLightning(l_playing, midimap::midimap.stopping);
+ break;
+
+ case ChannelStatus::PLAY:
+ kernelMidi::sendMidiLightning(l_playing, audible ? midimap::midimap.playing : midimap::midimap.playingInaudible);
+ break;
+
+ default: break;
+ }
+}
+}} // giada::m::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_CHANNEL_MIDI_LIGHTER_H
+#define G_CHANNEL_MIDI_LIGHTER_H
+
+
+#include <memory>
+
+
+namespace giada {
+namespace m
+{
+namespace mixer
+{
+struct Event;
+}
+struct MidiLighterState;
+
+/* MidiLighter
+Learns and emits MIDI lightning messages to physical hardware on events. */
+
+class MidiLighter
+{
+public:
+
+ MidiLighter(ChannelState*);
+ MidiLighter(const patch::Channel&, ChannelState*);
+ MidiLighter(const MidiLighter&, ChannelState* c=nullptr);
+
+ void parse(const mixer::Event& e, bool audible) const;
+
+ /* state
+ Pointer to mutable MidiLighterState state. */
+
+ std::unique_ptr<MidiLighterState> state;
+
+private:
+
+ void sendMute(uint32_t l_mute) const;
+ void sendSolo(uint32_t l_solo) const;
+ void sendStatus(uint32_t l_playing, bool audible) const;
+
+ ChannelState* m_channelState;
+};
+}} // giada::m::
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+
+#include "core/mixer.h"
+#include "core/plugins/pluginHost.h"
+#include "core/channels/state.h"
+#include "midiReceiver.h"
+
+
+namespace giada {
+namespace m
+{
+MidiReceiver::MidiReceiver(ChannelState* c)
+: state (std::make_unique<MidiReceiverState>())
+, m_channelState (c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MidiReceiver::MidiReceiver(const patch::Channel& /*p*/, ChannelState* c)
+: state (std::make_unique<MidiReceiverState>())
+, m_channelState (c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MidiReceiver::MidiReceiver(const MidiReceiver& /*o*/, ChannelState* c)
+: state (std::make_unique<MidiReceiverState>())
+, m_channelState (c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiReceiver::parse(const mixer::Event& e) const
+{
+ switch (e.type) {
+
+ case mixer::EventType::MIDI:
+ parseMidi(e.action.event); break;
+
+ case mixer::EventType::ACTION:
+ if (m_channelState->isPlaying())
+ sendToPlugins(e.action.event, e.delta);
+ break;
+
+ case mixer::EventType::KEY_KILL:
+ case mixer::EventType::SEQUENCER_STOP:
+ case mixer::EventType::SEQUENCER_REWIND:
+ sendToPlugins(MidiEvent(G_MIDI_ALL_NOTES_OFF), 0); break;
+
+ default: break;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiReceiver::render(const std::vector<ID>& pluginIds) const
+{
+ pluginHost::processStack(m_channelState->buffer, pluginIds, &state->midiBuffer);
+ state->midiBuffer.clear();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiReceiver::parseMidi(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(flat, /*delta=*/0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiReceiver::sendToPlugins(const MidiEvent& e, Frame localFrame) const
+{
+ juce::MidiMessage message = juce::MidiMessage(
+ e.getStatus(),
+ e.getNote(),
+ e.getVelocity());
+ state->midiBuffer.addEvent(message, localFrame);
+}
+}} // giada::m::
+
+
+#endif // WITH_VST
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_CHANNEL_MIDI_RECEIVER_H
+#define G_CHANNEL_MIDI_RECEIVER_H
+
+
+#ifdef WITH_VST
+
+
+#include <memory>
+
+
+namespace giada {
+namespace m
+{
+namespace mixer
+{
+struct Event;
+}
+struct MidiReceiverState;
+
+/* MidiReceiver
+Takes live action gestures AND recorded actions and redirect them as MIDI events
+to plug-in soft synths. */
+
+class MidiReceiver
+{
+public:
+
+ MidiReceiver(ChannelState*);
+ MidiReceiver(const patch::Channel&, ChannelState*);
+ MidiReceiver(const MidiReceiver&, ChannelState* c=nullptr);
+
+ void parse(const mixer::Event& e) const;
+ void render(const std::vector<ID>& pluginIds) const;
+
+ /* state
+ Pointer to mutable MidiReceiverState state. */
+
+ std::unique_ptr<MidiReceiverState> state;
+
+private:
+
+ /* parseMidi
+ Takes a live message (e.g. from a MIDI keyboard), strips it and sends it
+ to plug-ins. */
+
+ void parseMidi(const MidiEvent& e) const;
+
+ /* sendToPlugins
+ Enqueues the MIDI event for plug-ins processing. This will be read later on
+ by the PluginHost. */
+
+ void sendToPlugins(const MidiEvent& e, Frame localFrame) const;
+
+ ChannelState* m_channelState;
+};
+}} // giada::m::
+
+
+#endif // WITH_VST
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "core/mixer.h"
+#include "core/kernelMidi.h"
+#include "core/channels/state.h"
+#include "midiSender.h"
+
+
+namespace giada {
+namespace m
+{
+MidiSender::MidiSender(ChannelState* c)
+: state(std::make_unique<MidiSenderState>())
+, m_channelState(c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MidiSender::MidiSender(const patch::Channel& p, ChannelState* c)
+: state(std::make_unique<MidiSenderState>(p))
+, m_channelState(c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MidiSender::MidiSender(const MidiSender& o, ChannelState* c)
+: state(std::make_unique<MidiSenderState>(*o.state))
+, m_channelState(c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiSender::parse(const mixer::Event& e) const
+{
+ bool isPlaying = m_channelState->isPlaying();
+ bool isEnabled = state->enabled.load();
+
+ if (!isPlaying || !isEnabled)
+ return;
+
+ if (e.type == mixer::EventType::KEY_KILL ||
+ e.type == mixer::EventType::SEQUENCER_STOP)
+ send(MidiEvent(G_MIDI_ALL_NOTES_OFF));
+ else
+ if (e.type == mixer::EventType::ACTION)
+ send(e.action.event);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void MidiSender::send(MidiEvent e) const
+{
+ e.setChannel(state->filter.load());
+ kernelMidi::send(e.getRaw());
+}
+}} // giada::m::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_CHANNEL_MIDI_SENDER_H
+#define G_CHANNEL_MIDI_SENDER_H
+
+
+namespace giada {
+namespace m
+{
+namespace mixer
+{
+struct Event;
+}
+struct ChannelState;
+struct MidiSenderState;
+class MidiSender
+{
+public:
+
+ MidiSender(ChannelState*);
+ MidiSender(const patch::Channel&, ChannelState*);
+ MidiSender(const MidiSender&, ChannelState* c=nullptr);
+
+ void parse(const mixer::Event& e) const;
+
+ /* state
+ Pointer to mutable MidiSenderState state. */
+
+ std::unique_ptr<MidiSenderState> state;
+
+private:
+
+ void send(MidiEvent e) const;
+
+ ChannelState* m_channelState;
+};
+}} // giada::m::
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cassert>
+#include "core/action.h"
+#include "core/clock.h"
+#include "core/conf.h"
+#include "core/mixer.h"
+#include "core/recorderHandler.h"
+#include "core/recManager.h"
+#include "core/channels/state.h"
+#include "sampleActionRecorder.h"
+
+
+namespace giada {
+namespace m
+{
+SampleActionRecorder::SampleActionRecorder(ChannelState* c, SamplePlayerState* sc)
+: m_channelState(c)
+, m_samplePlayerState(sc)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+SampleActionRecorder::SampleActionRecorder(const SampleActionRecorder& /*o*/,
+ ChannelState* c, SamplePlayerState* sc)
+: SampleActionRecorder(c, sc)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleActionRecorder::parse(const mixer::Event& e) const
+{
+ assert(m_channelState != nullptr);
+
+ switch (e.type) {
+
+ case mixer::EventType::KEY_PRESS:
+ onKeyPress(); break;
+
+ /* Record a stop event only if channel is SINGLE_PRESS. For any other
+ mode the key release event is meaningless. */
+
+ case mixer::EventType::KEY_RELEASE:
+ if (canRecord() && m_samplePlayerState->mode.load() == SamplePlayerMode::SINGLE_PRESS)
+ record(MidiEvent::NOTE_OFF);
+ break;
+
+ case mixer::EventType::KEY_KILL:
+ if (canRecord())
+ record(MidiEvent::NOTE_KILL);
+ break;
+
+ case mixer::EventType::SEQUENCER_FIRST_BEAT:
+ onFirstBeat(); break;
+
+ case mixer::EventType::CHANNEL_TOGGLE_READ_ACTIONS:
+ toggleReadActions(); break;
+
+ case mixer::EventType::CHANNEL_KILL_READ_ACTIONS:
+ killReadActions(); break;
+
+ default: break;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleActionRecorder::record(int note) const
+{
+ recorderHandler::liveRec(m_channelState->id, MidiEvent(note, 0, 0),
+ clock::quantize(clock::getCurrentFrame()));
+
+ m_channelState->hasActions = true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleActionRecorder::startReadActions() const
+{
+ if (conf::conf.treatRecsAsLoops)
+ m_channelState->recStatus.store(ChannelStatus::WAIT);
+ else {
+ m_channelState->recStatus.store(ChannelStatus::PLAY);
+ m_channelState->readActions.store(true);
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleActionRecorder::stopReadActions(ChannelStatus curRecStatus) const
+{
+ /* First of all, if the clock is not running or treatRecsAsLoops is off,
+ just stop and disable everything. Otherwise make sure a channel with actions
+ behave like a dynamic one. */
+
+ if (!clock::isRunning() || !conf::conf.treatRecsAsLoops) {
+ m_channelState->recStatus.store(ChannelStatus::OFF);
+ m_channelState->readActions.store(false);
+ }
+ else
+ if (curRecStatus == ChannelStatus::WAIT)
+ m_channelState->recStatus.store(ChannelStatus::OFF);
+ else
+ if (curRecStatus == ChannelStatus::ENDING)
+ m_channelState->recStatus.store(ChannelStatus::PLAY);
+ else
+ m_channelState->recStatus.store(ChannelStatus::ENDING);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleActionRecorder::toggleReadActions() const
+{
+ /* When you start reading actions while conf::treatRecsAsLoops is true, the
+ value ch.state->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. */
+
+ if (!m_channelState->hasActions)
+ return;
+
+ bool readActions = m_channelState->readActions.load();
+ ChannelStatus recStatus = m_channelState->recStatus.load();
+
+ if (readActions || (!readActions && recStatus == ChannelStatus::WAIT))
+ stopReadActions(recStatus);
+ else
+ startReadActions();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleActionRecorder::killReadActions() const
+{
+ /* Killing Read Actions, i.e. shift + click on 'R' button is meaninful only
+ when the conf::treatRecsAsLoops is true. */
+
+ if (!conf::conf.treatRecsAsLoops)
+ return;
+ m_channelState->recStatus.store(ChannelStatus::OFF);
+ m_channelState->readActions.store(false);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleActionRecorder::onKeyPress() const
+{
+ if (!canRecord())
+ return;
+ record(MidiEvent::NOTE_ON);
+
+ /* Skip reading actions when recording on ChannelMode::SINGLE_PRESS to
+ prevent existing actions to interfere with the keypress/keyrel combo. */
+
+ if (m_samplePlayerState->mode.load() == SamplePlayerMode::SINGLE_PRESS)
+ m_channelState->readActions = false;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleActionRecorder::onKeyRelease() const
+{
+ if (canRecord() && m_samplePlayerState->mode.load() == SamplePlayerMode::SINGLE_PRESS) {
+ record(MidiEvent::NOTE_OFF);
+ m_channelState->readActions = true;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleActionRecorder::onFirstBeat() const
+{
+ ChannelStatus recStatus = m_channelState->recStatus.load();
+
+ switch (recStatus) {
+
+ case ChannelStatus::ENDING:
+ m_channelState->recStatus.store(ChannelStatus::OFF);
+ m_channelState->readActions = false;
+ break;
+
+ case ChannelStatus::WAIT:
+ m_channelState->recStatus.store(ChannelStatus::PLAY);
+ m_channelState->readActions = true;
+ break;
+
+ default: break;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool SampleActionRecorder::canRecord() const
+{
+ return recManager::isRecordingAction() &&
+ clock::isRunning() &&
+ !recManager::isRecordingInput() &&
+ !m_samplePlayerState->isAnyLoopMode();
+}
+}} // giada::m::
+
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_CHANNEL_SAMPLE_ACTION_RECORDER_H
+#define G_CHANNEL_SAMPLE_ACTION_RECORDER_H
+
+
+#include "core/types.h"
+
+
+namespace giada {
+namespace m
+{
+namespace mixer
+{
+struct Event;
+}
+struct ChannelState;
+
+/* SampleActionRecorder
+Records actions for channels and optionally manages the 'read action' state ('R'
+button on Sample Channels). */
+
+class SampleActionRecorder
+{
+public:
+
+ SampleActionRecorder(ChannelState*, SamplePlayerState*);
+ SampleActionRecorder(const SampleActionRecorder&, ChannelState* c=nullptr,
+ SamplePlayerState* sc=nullptr);
+
+ void parse(const mixer::Event& e) const;
+
+private:
+ void record(int note) const;
+ void onKeyPress() const;
+ void onKeyRelease() const;
+ void onFirstBeat() const;
+
+ void toggleReadActions() const;
+ void startReadActions() const;
+ void stopReadActions(ChannelStatus curRecStatus) const;
+ void killReadActions() const;
+
+ bool canRecord() const;
+
+ ChannelState* m_channelState;
+ SamplePlayerState* m_samplePlayerState;
+};
+}} // giada::m::
+
+
+#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <cassert>
-#include "utils/log.h"
-#include "core/const.h"
-#include "core/wave.h"
-#include "core/model/model.h"
-#include "sampleChannelProc.h"
-#include "sampleChannelRec.h"
-#include "channelManager.h"
-#include "sampleChannel.h"
-
-
-namespace giada {
-namespace m
-{
-SampleChannel::SampleChannel(bool inputMonitor, int bufferSize,
- ID columnId, ID id)
-: Channel (ChannelType::SAMPLE, ChannelStatus::EMPTY, bufferSize,
- columnId, id),
- hasWave (false),
- waveId (0),
- shift (0),
- mode (ChannelMode::SINGLE_BASIC),
- quantizing (false),
- inputMonitor (inputMonitor),
- pitch (G_DEFAULT_PITCH),
- tracker (0),
- trackerPreview (0),
- begin (0),
- end (0),
- midiInVeloAsVol (false),
- midiInReadActions(0x0),
- midiInPitch (0x0),
- bufferOffset (0),
- rewinding (false),
- rsmp_state (src_new(SRC_LINEAR, G_MAX_IO_CHANS, nullptr))
-{
- if (rsmp_state == nullptr) {
- u::log::print("[SampleChannel] unable to alloc memory for SRC_STATE!\n");
- throw std::bad_alloc();
- }
- bufferPreview.alloc(bufferSize, G_MAX_IO_CHANS);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-SampleChannel::SampleChannel(const SampleChannel& o)
-: Channel (o),
- hasWave (o.hasWave),
- waveId (o.waveId),
- shift (o.shift),
- mode (o.mode),
- quantizing (o.quantizing),
- inputMonitor (o.inputMonitor),
- pitch (o.pitch),
- tracker (o.tracker),
- trackerPreview (0),
- begin (o.begin),
- end (o.end),
- midiInVeloAsVol (o.midiInVeloAsVol),
- midiInReadActions(o.midiInReadActions),
- midiInPitch (o.midiInPitch),
- bufferOffset (o.bufferOffset),
- rewinding (o.rewinding),
- rsmp_state (src_new(SRC_LINEAR, G_MAX_IO_CHANS, nullptr))
-{
- if (rsmp_state == nullptr) {
- u::log::print("[SampleChannel] unable to alloc memory for SRC_STATE!\n");
- throw std::bad_alloc();
- }
-
- bufferPreview.alloc(o.bufferPreview.countFrames(), G_MAX_IO_CHANS);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-SampleChannel::SampleChannel(const patch::Channel& p, int bufferSize)
-: Channel (p, bufferSize),
- hasWave (p.waveId != 0),
- waveId (p.waveId),
- shift (0), // TODO
- mode (p.mode),
- quantizing (false),
- inputMonitor (p.inputMonitor),
- pitch (p.pitch),
- tracker (0),
- trackerPreview (0),
- begin (p.begin),
- end (p.end),
- midiInVeloAsVol (p.midiInVeloAsVol),
- midiInReadActions(p.midiInReadActions),
- midiInPitch (p.midiInPitch),
- bufferOffset (0),
- rewinding (0),
- rsmp_state (src_new(SRC_LINEAR, G_MAX_IO_CHANS, nullptr))
-{
- if (rsmp_state == nullptr) {
- u::log::print("[SampleChannel] unable to alloc memory for SRC_STATE!\n");
- throw std::bad_alloc();
- }
-
- bufferPreview.alloc(bufferSize, G_MAX_IO_CHANS);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-SampleChannel* SampleChannel::clone() const
-{
- return new SampleChannel(*this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-SampleChannel::~SampleChannel()
-{
- if (rsmp_state != nullptr)
- src_delete(rsmp_state);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::parseEvents(mixer::FrameEvents fe)
-{
- sampleChannelProc::parseEvents(this, fe);
- sampleChannelRec::parseEvents(this, fe);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::render(AudioBuffer& out, const AudioBuffer& in,
- AudioBuffer& inToOut, bool audible, bool running)
-{
- sampleChannelProc::render(this, out, in, inToOut, audible, running);
-}
-
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::rewindBySeq()
-{
- sampleChannelProc::rewindBySeq(this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::start(int localFrame, bool doQuantize, int velocity)
-{
- sampleChannelProc::start(this, localFrame, doQuantize, velocity);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::stop()
-{
- sampleChannelProc::stop(this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::stopBySeq(bool chansStopOnSeqHalt)
-{
- sampleChannelProc::stopBySeq(this, chansStopOnSeqHalt);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::kill(int localFrame)
-{
- sampleChannelProc::kill(this, localFrame);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool SampleChannel::recordStart(bool canQuantize)
-{
- return sampleChannelRec::recordStart(this, canQuantize);
-}
-
-
-bool SampleChannel::recordKill()
-{
- return sampleChannelRec::recordKill(this);
-}
-
-
-void SampleChannel::recordStop()
-{
- sampleChannelRec::recordStop(this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::startReadingActions(bool treatRecsAsLoops, bool recsStopOnChanHalt)
-{
- sampleChannelRec::startReadingActions(this, treatRecsAsLoops, recsStopOnChanHalt);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::stopReadingActions(bool running, bool treatRecsAsLoops,
- bool recsStopOnChanHalt)
-{
- sampleChannelRec::stopReadingActions(this, running, treatRecsAsLoops,
- recsStopOnChanHalt);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::stopInputRec(int globalFrame)
-{
- sampleChannelProc::stopInputRec(this, globalFrame);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::setMute(bool value)
-{
- sampleChannelProc::setMute(this, value);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::setSolo(bool value)
-{
- sampleChannelProc::setSolo(this, value);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::setReadActions(bool v, bool recsStopOnChanHalt)
-{
- sampleChannelRec::setReadActions(this, v, recsStopOnChanHalt);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool SampleChannel::hasLogicalData() const
-{
- if (!hasWave)
- return false;
-
- model::WavesLock wl(model::waves);
- return model::get(model::waves, waveId).isLogical();
-};
-
-
-bool SampleChannel::hasEditedData() const
-{
- if (!hasWave)
- return false;
-
- model::WavesLock wl(model::waves);
- return model::get(model::waves, waveId).isEdited();
-};
-
-
-bool SampleChannel::hasData() const
-{
- return hasWave;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::setBegin(int f)
-{
- model::WavesLock lock(model::waves);
- const Wave& wave = model::get(model::waves, waveId);
-
- if (f < 0)
- f = 0;
- else
- if (f > wave.getSize())
- f = wave.getSize();
- else
- if (f >= end)
- f = end - 1;
-
- begin = f;
- tracker = f;
- trackerPreview = f;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::setEnd(int f)
-{
- model::WavesLock lock(model::waves);
- const Wave& wave = model::get(model::waves, waveId);
-
- if (f >= wave.getSize())
- f = wave.getSize() - 1;
- else
- if (f <= begin)
- f = begin + 1;
-
- end = f;}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int SampleChannel::getBegin() const { return begin; }
-int SampleChannel::getEnd() const { return end; }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::setPitch(float v)
-{
- if (v > G_MAX_PITCH)
- pitch = G_MAX_PITCH;
- else
- if (v < G_MIN_PITCH)
- pitch = G_MIN_PITCH;
- else
- pitch = v;
-
-// ???? /* if status is off don't slide between frequencies */
-// ????
-// ???? if (status & (STATUS_OFF | STATUS_WAIT))
-// ???? src_set_ratio(rsmp_state, 1/pitch);
-}
-
-
-float SampleChannel::getPitch() const { return pitch; }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int SampleChannel::getPosition() const
-{
- if (playStatus != ChannelStatus::EMPTY &&
- playStatus != ChannelStatus::MISSING &&
- playStatus != ChannelStatus::OFF)
- return tracker - begin;
- else
- return -1;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::empty()
-{
- playStatus = ChannelStatus::EMPTY;
- begin = 0;
- end = 0;
- tracker = 0;
- volume = G_DEFAULT_VOL;
- hasActions = false;
- hasWave = false;
- waveId = 0;
- sendMidiLstatus();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void SampleChannel::pushWave(ID wid, Frame size)
-{
- playStatus = ChannelStatus::OFF;
- waveId = wid;
- begin = 0;
- end = size;
- tracker = 0;
- hasWave = true;
- sendMidiLstatus();
-}
-
-
-void SampleChannel::popWave()
-{
- playStatus = ChannelStatus::OFF;
- waveId = 0;
- begin = 0;
- end = 0;
- tracker = 0;
- hasWave = false;
- sendMidiLstatus();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-std::string SampleChannel::getSamplePath() const
-{
- if (!hasWave)
- return "";
-
- model::WavesLock wl(model::waves);
- return model::get(model::waves, waveId).getPath();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool SampleChannel::canInputRec() const
-{
- return !hasWave && armed == true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int SampleChannel::fillBuffer(AudioBuffer& dest, int start, int offset)
-{
- assert(offset < dest.countFrames());
-
- if (pitch == 1.0) return fillBufferCopy(dest, start, offset);
- else return fillBufferResampled(dest, start, offset);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int SampleChannel::fillBufferResampled(AudioBuffer& dest, int start, int offset)
-{
- model::WavesLock lock(model::waves);
- const Wave& wave = model::get(model::waves, waveId);
-
- rsmp_data.data_in = wave.getFrame(start); // Source data
- rsmp_data.input_frames = end - start; // How many readable frames
- rsmp_data.data_out = dest[offset]; // Destination (processed data)
- rsmp_data.output_frames = dest.countFrames() - offset; // How many frames to process
- rsmp_data.end_of_input = false;
- rsmp_data.src_ratio = 1 / pitch;
-
- src_process(rsmp_state, &rsmp_data);
-
- return rsmp_data.input_frames_used; // Returns used frames
-}
-
-/* -------------------------------------------------------------------------- */
-
-
-int SampleChannel::fillBufferCopy(AudioBuffer& dest, int start, int offset)
-{
- model::WavesLock lock(model::waves);
- const Wave& wave = model::get(model::waves, waveId);
-
- int used = dest.countFrames() - offset;
- if (used > wave.getSize() - start)
- used = wave.getSize() - start;
-
- dest.copyData(wave.getFrame(start), used, offset);
-
- return used;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool SampleChannel::isAnyLoopMode() const
-{
- return mode == ChannelMode::LOOP_BASIC ||
- mode == ChannelMode::LOOP_ONCE ||
- mode == ChannelMode::LOOP_REPEAT ||
- mode == ChannelMode::LOOP_ONCE_BAR;
-}
-
-
-bool SampleChannel::isAnySingleMode() const
-{
- return !isAnyLoopMode();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool SampleChannel::isOnLastFrame() const
-{
- return tracker >= end;
-}
-
-}} // giada::m::
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef G_SAMPLE_CHANNEL_H
-#define G_SAMPLE_CHANNEL_H
-
-
-#include <memory>
-#include <functional>
-#include <samplerate.h>
-#include "core/types.h"
-#include "core/channels/channel.h"
-
-
-namespace giada {
-namespace m
-{
-class Wave;
-
-class SampleChannel : public Channel
-{
-public:
-
- SampleChannel(bool inputMonitor, int bufferSize, ID columnId, ID id);
- SampleChannel(const SampleChannel& o);
- SampleChannel(const patch::Channel& p, int bufferSize);
- ~SampleChannel();
-
- SampleChannel* clone() const override;
- void parseEvents(mixer::FrameEvents fe) override;
- void render(AudioBuffer& out, const AudioBuffer& in, AudioBuffer& inToOut,
- bool audible, bool running) override;
-
- void start(int frame, bool doQuantize, int velocity) override;
- void stop() override;
- void kill(int frame) override;
- bool recordStart(bool canQuantize) override;
- bool recordKill() override;
- void recordStop() override;
- void setMute(bool value) override;
- void setSolo(bool value) override;
- void startReadingActions(bool treatRecsAsLoops, bool recsStopOnChanHalt) override;
- void stopReadingActions(bool running, bool treatRecsAsLoops,
- bool recsStopOnChanHalt) override;
- void empty() override;
- void stopBySeq(bool chansStopOnSeqHalt) override;
- void rewindBySeq() override;
- void stopInputRec(int globalFrame) override;
- bool canInputRec() const override;
- bool hasLogicalData() const override;
- bool hasEditedData() const override;
- bool hasData() const override;
-
- int getBegin() const;
- int getEnd() const;
- float getPitch() const;
- bool isAnyLoopMode() const;
- bool isAnySingleMode() const;
- bool isOnLastFrame() const;
- std::string getSamplePath() const;
-
- /* getPosition
- Returns the position of an active sample. If EMPTY o MISSING returns -1. */
-
- int getPosition() const;
-
- /* fillBuffer
- Fills 'dest' buffer at point 'offset' with Wave data taken from 'start'.
- Returns how many frames have been used from the original Wave data. It also
- resamples data if pitch != 1.0f. */
-
- int fillBuffer(AudioBuffer& dest, int start, int offset);
-
- /* pushWave
- Adds a new wave to this channel. */
-
- void pushWave(ID waveId, Frame waveSize);
- void popWave();
-
- void setPitch(float v);
- void setBegin(int f);
- void setEnd(int f);
-
- void setReadActions(bool v, bool recsStopOnChanHalt);
-
- /* bufferPreview
- Extra buffer for audio preview. */
-
- AudioBuffer bufferPreview;
-
- /* hasWave
- Tells if a wave is linked to this channel. */
- /* TODO - useless: check if waveId != 0 */
-
- bool hasWave;
-
- /* waveId
- ID of a Wave object. Might be useless if hasWave == false. */
-
- ID waveId;
-
- int shift;
- ChannelMode mode;
- bool quantizing; // quantization in progress
- bool inputMonitor;
- float pitch;
-
- Frame tracker; // chan position
- Frame trackerPreview; // chan position for audio preview
-
- /* begin, end
- Begin/end point to read wave data from/to. */
-
- Frame begin;
- Frame end;
-
- /* midiIn*
- MIDI input parameters. */
-
- bool midiInVeloAsVol;
- uint32_t midiInReadActions;
- uint32_t midiInPitch;
-
- /* bufferOffset
- Offset used while filling the internal buffer with audio data. Value is
- greater than zero on start sample. */
-
- Frame bufferOffset;
-
- /* rewinding
- Tells whether a rewind event is taking place. Used to fill the audio
- buffer twice. */
-
- bool rewinding;
-
-private:
-
- /* rsmp_state, rsmp_data
- Structs from libsamplerate. */
-
- SRC_STATE* rsmp_state;
- SRC_DATA rsmp_data;
-
- int fillBufferResampled(AudioBuffer& dest, int start, int offset);
- int fillBufferCopy (AudioBuffer& dest, int start, int offset);
-};
-
-}} // giada::m::
-
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <cassert>
-#include "utils/math.h"
-#include "core/model/model.h"
-#include "core/channels/sampleChannel.h"
-#include "core/const.h"
-#include "core/pluginHost.h"
-#include "core/mixerHandler.h"
-#include "sampleChannelProc.h"
-
-
-namespace giada {
-namespace m {
-namespace sampleChannelProc
-{
-namespace
-{
-void rewind_(SampleChannel* ch, Frame localFrame)
-{
- /* Quantization stops on rewind. */
-
- ch->quantizing = false;
-
- if (ch->isPlaying()) {
- ch->rewinding = true;
- ch->bufferOffset = localFrame;
- }
- else
- ch->tracker = ch->begin;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* quantize
-Starts channel according to quantizer. */
-
-void quantize_(SampleChannel* ch, int localFrame)
-{
- switch (ch->playStatus) {
- case ChannelStatus::OFF:
- ch->playStatus = ChannelStatus::PLAY;
- ch->bufferOffset = localFrame;
- ch->sendMidiLstatus();
- // ch->quantizing = false is set by sampleChannelRec::quantize()
- break;
-
- default:
- rewind_(ch, localFrame);
- break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* onBar
-Things to do when the sequencer is on a bar. */
-
-void onBar_(SampleChannel* ch, int localFrame)
-{
- switch (ch->playStatus) {
- case ChannelStatus::PLAY:
- if (ch->mode == ChannelMode::LOOP_REPEAT)
- rewind_(ch, localFrame);
- break;
-
- case ChannelStatus::WAIT:
- if (ch->mode == ChannelMode::LOOP_ONCE_BAR) {
- ch->playStatus = ChannelStatus::PLAY;
- ch->bufferOffset = localFrame;
- ch->sendMidiLstatus();
- }
- break;
-
- default: break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* onFirstBeat
-Things to do when the sequencer is on the first beat. */
-
-void onFirstBeat_(SampleChannel* ch, Frame localFrame)
-{
- switch (ch->playStatus) {
- case ChannelStatus::PLAY:
- if (ch->isAnyLoopMode())
- rewind_(ch, localFrame);
- break;
-
- case ChannelStatus::WAIT:
- ch->playStatus = ChannelStatus::PLAY;
- ch->bufferOffset = localFrame;
- ch->sendMidiLstatus();
- break;
-
- case ChannelStatus::ENDING:
- if (ch->isAnyLoopMode())
- kill(ch, localFrame);
-
- default: break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* onLastFrame
-Things to do when the sample has reached the end (i.e. last frame). Called by
-prepareBuffer(). */
-
-void onLastFrame_(SampleChannel* ch, bool running)
-{
- switch (ch->playStatus) {
- 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 ((ch->mode == ChannelMode::SINGLE_BASIC ||
- ch->mode == ChannelMode::SINGLE_PRESS ||
- ch->mode == ChannelMode::SINGLE_RETRIG) ||
- (ch->isAnyLoopMode() && !running))
- ch->playStatus = ChannelStatus::OFF;
- else
- if (ch->mode == ChannelMode::LOOP_ONCE ||
- ch->mode == ChannelMode::LOOP_ONCE_BAR)
- ch->playStatus = ChannelStatus::WAIT;
- ch->sendMidiLstatus();
- break;
-
- case ChannelStatus::ENDING:
- /* LOOP_ONCE or LOOP_ONCE_BAR: if ending (i.e. the user requested
- their termination), stop 'em. Let them wait otherwise. */
- if (ch->mode == ChannelMode::LOOP_ONCE ||
- ch->mode == ChannelMode::LOOP_ONCE_BAR)
- ch->playStatus = ChannelStatus::WAIT;
- else {
- ch->playStatus = ChannelStatus::OFF;
- ch->sendMidiLstatus();
- }
- break;
-
- default: break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void processIO_(SampleChannel* ch, m::AudioBuffer& out, const m::AudioBuffer& in,
- bool running)
-{
- assert(out.countSamples() == ch->buffer.countSamples());
- if (in.isAllocd())
- assert(in.countSamples() == ch->buffer.countSamples());
-
- /* If armed and input buffer is not empty (i.e. input device available) and
- input monitor is on, copy input buffer to channel buffer: this enables the
- input monitoring. The channel buffer will be overwritten later on by
- pluginHost::processStack, so that you would record "clean" audio
- (i.e. not plugin-processed). */
-
- if (ch->armed && in.isAllocd() && ch->inputMonitor) {
- for (int i=0; i<ch->buffer.countFrames(); i++)
- for (int j=0; j<ch->buffer.countChannels(); j++)
- ch->buffer[i][j] += in[i][j]; // add, don't overwrite
- }
-
-#ifdef WITH_VST
- pluginHost::processStack(ch->buffer, ch->pluginIds);
-#endif
-
- for (int i=0; i<out.countFrames(); i++) {
- if (running)
- ch->calcVolumeEnvelope();
- if (!ch->mute)
- for (int j=0; j<out.countChannels(); j++)
- out[i][j] += ch->buffer[i][j] * ch->volume * ch->volume_i * ch->calcPanning(j);
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void processPreview_(SampleChannel* ch, m::AudioBuffer& out)
-{
- ch->bufferPreview.clear();
-
- /* If the tracker exceedes the end point and preview is looped, split the
- rendering as in SampleChannel::reset(). */
-
- if (ch->trackerPreview == ch->end)
- ch->trackerPreview = ch->begin;
- else
- if (ch->trackerPreview + ch->bufferPreview.countFrames() >= ch->end) {
- int offset = ch->end - ch->trackerPreview;
- ch->trackerPreview += ch->fillBuffer(ch->bufferPreview, ch->trackerPreview, 0);
- ch->trackerPreview = ch->begin;
- if (ch->previewMode == PreviewMode::LOOP)
- ch->trackerPreview += ch->fillBuffer(ch->bufferPreview, ch->begin, offset);
- else
- if (ch->previewMode == PreviewMode::NORMAL)
- ch->previewMode = PreviewMode::NONE;
- }
- else
- ch->trackerPreview += ch->fillBuffer(ch->bufferPreview, ch->trackerPreview, 0);
-
- for (int i=0; i<out.countFrames(); i++)
- for (int j=0; j<out.countChannels(); j++)
- out[i][j] += ch->bufferPreview[i][j] * ch->volume * ch->calcPanning(j);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void fillBuffer_(SampleChannel* ch, bool running)
-{
- ch->buffer.clear();
-
- if (!ch->hasData() || !ch->isPlaying())
- return;
-
- if (ch->rewinding) {
-
- /* Fill the tail. */
-
- if (!ch->isOnLastFrame())
- ch->fillBuffer(ch->buffer, ch->tracker, 0);
-
- /* Reset tracker to begin point. */
-
- ch->tracker = ch->begin;
-
- /* Then fill the new head. */
-
- ch->tracker += ch->fillBuffer(ch->buffer, ch->tracker, ch->bufferOffset);
- ch->bufferOffset = 0;
- ch->rewinding = false;
- }
- else {
- Frame framesUsed = ch->fillBuffer(ch->buffer, ch->tracker, ch->bufferOffset);
- ch->tracker += framesUsed;
- ch->bufferOffset = 0;
- if (ch->isOnLastFrame()) {
- onLastFrame_(ch, running);
- ch->tracker = ch->begin;
- if (ch->mode == ChannelMode::LOOP_BASIC ||
- ch->mode == ChannelMode::LOOP_REPEAT ||
- ch->mode == ChannelMode::SINGLE_ENDLESS) {
- /* framesUsed might be imprecise when working with resampled
- audio, which could cause a buffer overflow if used as offset.
- Let's clamp it to be at most buffer->countFrames(). */
- ch->tracker += ch->fillBuffer(ch->buffer, ch->tracker,
- u::math::bound(framesUsed, 0, ch->buffer.countFrames() - 1));
- }
- }
- }
-}
-}; // {anonymous}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-void kill(SampleChannel* ch, int localFrame)
-{
- switch (ch->playStatus) {
- case ChannelStatus::WAIT:
- case ChannelStatus::PLAY:
- case ChannelStatus::ENDING:
- /* Clear data in range [localFrame, (buffer.size)) if the kill event
- occurs in the middle of the buffer. */
- if (localFrame != 0)
- ch->buffer.clear(localFrame);
- ch->playStatus = ChannelStatus::OFF;
- ch->sendMidiLstatus();
- rewind_(ch, localFrame);
- break;
-
- default: break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void stop(SampleChannel* ch)
-{
- switch (ch->playStatus) {
- case ChannelStatus::PLAY:
- if (ch->mode == ChannelMode::SINGLE_PRESS)
- kill(ch, 0);
- break;
-
- default:
- /* If quantizing, stop a SINGLE_PRESS immediately. */
- if (ch->mode == ChannelMode::SINGLE_PRESS && ch->quantizing)
- ch->quantizing = false;
- break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void stopInputRec(SampleChannel* ch, int globalFrame)
-{
- /* Start all sample channels in loop mode that were armed, i.e. that were
- recording stuff and not yet in play. They are also started in force mode, i.e.
- they must start playing right away at the current global frame, not at the
- next first beat. */
- if (ch->isAnyLoopMode() && ch->playStatus == ChannelStatus::OFF && ch->armed) {
- ch->playStatus = ChannelStatus::PLAY;
- ch->tracker = globalFrame;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void stopBySeq(SampleChannel* ch, bool chansStopOnSeqHalt)
-{
- switch (ch->playStatus) {
- case ChannelStatus::WAIT:
- /* Loop-mode channels in wait status get stopped right away. */
- if (ch->isAnyLoopMode())
- ch->playStatus = ChannelStatus::OFF;
- break;
-
- case ChannelStatus::PLAY:
- /* Kill samples if a) chansStopOnSeqHalt == true (run the sample to end
- otherwise); b) when a channel is reading (and playing) actions. */
- if (chansStopOnSeqHalt)
- if (ch->isAnyLoopMode() || ch->isReadingActions())
- kill(ch, 0);
- break;
-
- default: break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void rewindBySeq(SampleChannel* ch)
-{
- /* Rewind LOOP_ANY or SINGLE_ANY only if it's in read-record-mode. Rewind by
- sequencer is a user-generated event, it always occurs on local frame 0. */
-
- if (ch->hasData()) {
- if ((ch->isAnyLoopMode()) || (ch->recStatus == ChannelStatus::PLAY && (ch->isAnySingleMode())))
- rewind_(ch, 0);
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setMute(SampleChannel* ch, bool value)
-{
- ch->mute = value;
-
- // This is for processing playing_inaudible
- ch->sendMidiLstatus();
-
- ch->sendMidiLmute();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setSolo(SampleChannel* ch, bool value)
-{
- ch->solo = value;
-
- // This is for processing playing_inaudible
- model::ChannelsLock l(model::channels);
- for (Channel* c : model::channels)
- c->sendMidiLstatus();
-
- ch->sendMidiLsolo();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void start(SampleChannel* ch, int localFrame, bool doQuantize, int velocity)
-{
- /* For one-shot modes, velocity drives the internal volume. */
- if (velocity != 0) {
- if (ch->isAnySingleMode() && ch->midiInVeloAsVol)
- ch->volume_i = u::math::map<int, float>(velocity, 0, G_MAX_VELOCITY, 0.0, 1.0);
- }
-
- switch (ch->playStatus) {
- case ChannelStatus::OFF:
- ch->bufferOffset = localFrame;
- if (ch->isAnyLoopMode()) {
- ch->playStatus = ChannelStatus::WAIT;
- ch->sendMidiLstatus();
- }
- else {
- if (doQuantize)
- ch->quantizing = true;
- else {
- ch->playStatus = ChannelStatus::PLAY;
- ch->sendMidiLstatus();
- }
- }
- break;
-
- case ChannelStatus::PLAY:
- if (ch->mode == ChannelMode::SINGLE_RETRIG) {
- if (doQuantize)
- ch->quantizing = true;
- else
- rewind_(ch, localFrame);
- }
- else
- if (ch->isAnyLoopMode() || ch->mode == ChannelMode::SINGLE_ENDLESS) {
- ch->playStatus = ChannelStatus::ENDING;
- ch->sendMidiLstatus();
- }
- else
- if (ch->mode == ChannelMode::SINGLE_BASIC) {
- rewind_(ch, localFrame);
- ch->playStatus = ChannelStatus::OFF;
- ch->sendMidiLstatus();
- }
- break;
-
- case ChannelStatus::WAIT:
- ch->playStatus = ChannelStatus::OFF;
- ch->sendMidiLstatus();
- break;
-
- case ChannelStatus::ENDING:
- ch->playStatus = ChannelStatus::PLAY;
- ch->sendMidiLstatus();
- break;
-
- default: break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void render(SampleChannel* ch, AudioBuffer& out, const AudioBuffer& in,
- AudioBuffer& inToOut, bool audible, bool running)
-{
- fillBuffer_(ch, running);
-
- if (audible)
- processIO_(ch, out, in, running);
-
- if (ch->isPreview())
- processPreview_(ch, out);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void parseEvents(SampleChannel* ch, mixer::FrameEvents fe)
-{
- if (!ch->hasData())
- return;
-
- /* Quantize only if is single mode and in quantizer-wait mode and a
- quantizer step has passed. */
-
- if (ch->isAnySingleMode() && ch->quantizing && fe.quantoPassed)
- quantize_(ch, fe.frameLocal);
- if (fe.onBar)
- onBar_(ch, fe.frameLocal);
- if (fe.onFirstBeat)
- onFirstBeat_(ch, fe.frameLocal);
-}
-}}};
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef G_SAMPLE_CHANNEL_PROC_H
-#define G_SAMPLE_CHANNEL_PROC_H
-
-
-#include "core/mixer.h"
-#include "core/audioBuffer.h"
-#include "core/types.h"
-
-
-namespace giada {
-namespace m
-{
-class SampleChannel;
-
-namespace sampleChannelProc
-{
-/**/
-void render(SampleChannel* ch, AudioBuffer& out, const AudioBuffer& in,
- AudioBuffer& inToOut, bool audible, bool running);
-
-/* parseEvents
-Parses events gathered by Mixer::masterPlay(). */
-
-void parseEvents(SampleChannel* ch, mixer::FrameEvents ev);
-
-/* kill
-Stops a channel abruptly. */
-
-void kill(SampleChannel* ch, int localFrame);
-
-/* stop
-Stops a channel normally (via key or MIDI). */
-
-void stop(SampleChannel* ch);
-
-/* stopInputRec
-Prepare a channel for playing when the input recording is done. */
-
-void stopInputRec(SampleChannel* ch, int globalFrame);
-
-/* stopBySeq
-Stops a channel when the stop button on main transport is pressed. */
-
-void stopBySeq(SampleChannel* ch, bool chansStopOnSeqHalt);
-
-/* rewind
-Rewinds channel when rewind button on main transport is pressed. */
-
-void rewindBySeq(SampleChannel* ch);
-
-/* start
-Starts a channel. doQuantize = false (don't quantize) when Mixer is reading
-actions from Recorder. */
-
-void start(SampleChannel* ch, int localFrame, bool doQuantize, int velocity);
-
-void setMute(SampleChannel* ch, bool value);
-void setSolo(SampleChannel* ch, bool value);
-}}};
-
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <cassert>
-#include "utils/math.h"
-#include "core/channels/sampleChannel.h"
-#include "core/recorder.h"
-#include "core/recorderHandler.h"
-#include "core/recManager.h"
-#include "core/const.h"
-#include "core/conf.h"
-#include "core/clock.h"
-#include "core/action.h"
-#include "core/kernelAudio.h"
-#include "sampleChannelRec.h"
-
-
-namespace giada {
-namespace m {
-namespace sampleChannelRec
-{
-namespace
-{
-/* onFirstBeat
-Things to do when the sequencer is on the first beat. */
-
-void onFirstBeat_(SampleChannel* ch, bool recsStopOnChanHalt)
-{
- switch (ch->recStatus) {
- case ChannelStatus::ENDING:
- ch->recStatus = ChannelStatus::OFF;
- setReadActions(ch, false, recsStopOnChanHalt); // rec stop
- break;
-
- case ChannelStatus::WAIT:
- ch->recStatus = ChannelStatus::PLAY;
- setReadActions(ch, true, recsStopOnChanHalt); // rec start
- break;
-
- default: break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool recorderCanRec_(SampleChannel* ch)
-{
- /* Can record on a channel if:
- - recorder is on
- - mixer is running
- - mixer is not recording a take somewhere
- - channel is MIDI or SAMPLE type with data in it */
-
- return recManager::isRecordingAction() &&
- clock::isRunning() &&
- !recManager::isRecordingInput() &&
- (ch->type == ChannelType::MIDI || (ch->type == ChannelType::SAMPLE && ch->hasData()));
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-/* calcVolumeEnv
-Computes any changes in volume done via envelope tool. */
-
-void calcVolumeEnv_(SampleChannel* ch, const Action& a1)
-{
- assert(a1.next != nullptr);
-
- const Action a2 = *a1.next;
-
- double vf1 = u::math::map<int, double>(a1.event.getVelocity(), 0, G_MAX_VELOCITY, 0, 1.0);
- double vf2 = u::math::map<int, double>(a2.event.getVelocity(), 0, G_MAX_VELOCITY, 0, 1.0);
-
- ch->volume_i = vf1;
- ch->volume_d = a2.frame == a1.frame ? 0 : (vf2 - vf1) / (a2.frame - a1.frame);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void parseAction_(SampleChannel* ch, const Action& a, int localFrame, int globalFrame)
-{
- switch (a.event.getStatus()) {
- case MidiEvent::NOTE_ON:
- if (ch->isAnySingleMode())
- ch->start(localFrame, /*quantize=*/false, /*velocity=*/0);
- break;
- case MidiEvent::NOTE_OFF:
- if (ch->isAnySingleMode())
- ch->stop();
- break;
- case MidiEvent::NOTE_KILL:
- if (ch->isAnySingleMode())
- ch->kill(localFrame);
- break;
- case MidiEvent::ENVELOPE:
- calcVolumeEnv_(ch, a);
- break;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void recordKeyPressAction_(SampleChannel* ch)
-{
- if (!recorderCanRec_(ch))
- return;
-
- /* Disable reading actions while recording SINGLE_PRESS mode. Don't let
- existing actions interfere with the current one being recorded. */
-
- if (ch->mode == ChannelMode::SINGLE_PRESS)
- ch->readActions = false;
-
- recorderHandler::liveRec(ch->id, MidiEvent(MidiEvent::NOTE_ON, 0, 0));
- ch->hasActions = true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void quantize_(SampleChannel* ch, bool quantoPassed)
-{
- /* Skip if in loop mode or not in a quantization stage. Otherwise the
- quantization wait has expired: record the keypress. */
-
- if (!ch->isAnyLoopMode() && ch->quantizing && quantoPassed && ch->playStatus == ChannelStatus::PLAY) {
- ch->quantizing = false;
- recordKeyPressAction_(ch);
- }
-}
-}; // {anonymous}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-void parseEvents(SampleChannel* ch, mixer::FrameEvents fe)
-{
- if (!ch->hasWave)
- return;
- quantize_(ch, fe.quantoPassed);
- if (fe.onFirstBeat)
- onFirstBeat_(ch, conf::conf.recsStopOnChanHalt);
- if (ch->readActions && fe.actions != nullptr)
- for (const Action& action : *fe.actions)
- if (action.channelId == ch->id)
- parseAction_(ch, action, fe.frameLocal, fe.frameGlobal);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool recordStart(SampleChannel* ch, bool canQuantize)
-{
- /* Record a 'start' event if the quantizer is off, otherwise let mixer to
- handle it when a quantoWait has passed (see quantize_()). Also skip if
- channel is in any loop mode, where KEYPRESS and KEYREL are meaningless. */
-
- if (!canQuantize && !ch->isAnyLoopMode() && recorderCanRec_(ch))
- recordKeyPressAction_(ch);
- return true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool recordKill(SampleChannel* ch)
-{
- /* Don't record NOTE_KILL actions for LOOP channels. */
- if (recorderCanRec_(ch) && !ch->isAnyLoopMode()) {
- recorder::rec(ch->id, clock::getCurrentFrame(), MidiEvent(MidiEvent::NOTE_KILL, 0, 0));
- ch->hasActions = true;
- }
- return true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void recordStop(SampleChannel* ch)
-{
- /* Record a stop event only if channel is SINGLE_PRESS. For any other mode
- the stop event is meaningless. */
- if (recorderCanRec_(ch) && ch->mode == ChannelMode::SINGLE_PRESS)
- recorderHandler::liveRec(ch->id, MidiEvent(MidiEvent::NOTE_OFF, 0, 0));
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setReadActions(SampleChannel* ch, bool v, bool recsStopOnChanHalt)
-{
- ch->readActions = v;
- if (!v && recsStopOnChanHalt)
- ch->kill(0); // FIXME - wrong frame value
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void startReadingActions(SampleChannel* ch, bool treatRecsAsLoops, bool recsStopOnChanHalt)
-{
- if (treatRecsAsLoops)
- ch->recStatus = ChannelStatus::WAIT;
- else
- setReadActions(ch, true, recsStopOnChanHalt);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void stopReadingActions(SampleChannel* ch, bool isClockRunning, bool treatRecsAsLoops,
- bool recsStopOnChanHalt)
-{
- /* First of all, if the clock is not running just stop and disable everything.
- Then if "treatRecsAsLoop" wait until the sequencer reaches beat 0, so put the
- channel in REC_ENDING status. */
-
- if (!isClockRunning) {
- ch->recStatus = ChannelStatus::OFF;
- setReadActions(ch, false, false);
- }
- else
- if (ch->recStatus == ChannelStatus::WAIT)
- ch->recStatus = ChannelStatus::OFF;
- else
- if (ch->recStatus == ChannelStatus::ENDING)
- ch->recStatus = ChannelStatus::PLAY;
- else
- if (treatRecsAsLoops)
- ch->recStatus = ChannelStatus::ENDING;
- else
- setReadActions(ch, false, recsStopOnChanHalt);
-}
-}}};
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef G_SAMPLE_CHANNEL_REC_H
-#define G_SAMPLE_CHANNEL_REC_H
-
-
-namespace giada {
-namespace m
-{
-class SampleChannel;
-
-namespace sampleChannelRec
-{
-void parseEvents(SampleChannel* ch, mixer::FrameEvents fe);
-
-/* recordStart
-Records a 'start' action if capable of. Returns true if a start() call can
-be performed. */
-
-bool recordStart(SampleChannel* ch, bool doQuantize);
-
-/* recordKill
-Records a 'kill' action if capable of. Returns true if a kill() call can
-be performed. */
-
-bool recordKill(SampleChannel* ch);
-
-/* recordStop
-Ends overdub mode SINGLE_PRESS channels. */
-
-void recordStop(SampleChannel* ch);
-
-/* setReadActions
-If enabled (v == true), Recorder will read actions from channel 'ch'. If
-recsStopOnChanHalt == true and v == false, will also kill the channel. */
-
-void setReadActions(SampleChannel* ch, bool v, bool recsStopOnChanHalt);
-
-void startReadingActions(SampleChannel* ch, bool treatRecsAsLoops,
- bool recsStopOnChanHalt);
-void stopReadingActions(SampleChannel* ch, bool isClockRunning,
- bool treatRecsAsLoops, bool recsStopOnChanHalt);
-}}};
-
-
-#endif
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cassert>
+#include "core/conf.h"
+#include "core/clock.h"
+#include "core/action.h"
+#include "core/mixer.h"
+#include "core/channels/state.h"
+#include "utils/math.h"
+#include "sampleController.h"
+
+
+namespace giada {
+namespace m
+{
+namespace
+{
+constexpr int Q_ACTION_PLAY = 0;
+constexpr int Q_ACTION_REWIND = 1;
+} // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+SampleController::SampleController(ChannelState* c, SamplePlayerState* s)
+: m_channelState (c)
+, m_samplePlayerState(s)
+{
+ m_samplePlayerState->quantizer.schedule(Q_ACTION_PLAY,
+ [&status = m_channelState->playStatus, &offset = m_samplePlayerState->offset]
+ (Frame delta)
+ {
+ offset = delta;
+ status = ChannelStatus::PLAY;
+ });
+
+ m_samplePlayerState->quantizer.schedule(Q_ACTION_REWIND, [this] (Frame delta)
+ {
+ rewind(delta);
+ });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+SampleController::SampleController(const SampleController& /*o*/, ChannelState* c, SamplePlayerState* s)
+: SampleController(c, s)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleController::parse(const mixer::Event& e) const
+{
+ assert(m_channelState != nullptr);
+ assert(m_samplePlayerState != nullptr);
+
+ switch (e.type) {
+ case mixer::EventType::KEY_PRESS:
+ press(e.delta, e.action.event.getVelocity(), /*manual=*/true); break;
+
+ case mixer::EventType::KEY_RELEASE:
+ release(e.delta); break;
+
+ case mixer::EventType::KEY_KILL:
+ kill(e.delta); break;
+
+ case mixer::EventType::SEQUENCER_FIRST_BEAT:
+ if (clock::isRunning())
+ onFirstBeat(e.delta);
+ break;
+
+ case mixer::EventType::SEQUENCER_BAR:
+ onBar(e.delta); break;
+
+ case mixer::EventType::SEQUENCER_STOP:
+ onStopBySeq(); break;
+
+ case mixer::EventType::ACTION:
+ if (m_channelState->readActions.load() == true)
+ parseAction(e.action, e.delta);
+ break;
+
+ case mixer::EventType::CHANNEL_TOGGLE_READ_ACTIONS:
+ toggleReadActions(); break;
+
+ default: break;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleController::onLastFrame() const
+{
+ ChannelStatus playStatus = m_channelState->playStatus.load();
+ SamplePlayerMode mode = m_samplePlayerState->mode.load();
+ bool running = clock::isRunning();
+
+ if (playStatus == ChannelStatus::PLAY) {
+ /* Stop LOOP_* when the sequencer is off, or SINGLE_* except for
+ SINGLE_ENDLESS, which runs forever unless it's in ENDING mode.
+ Other loop once modes are put in wait mode. */
+ if ((mode == SamplePlayerMode::SINGLE_BASIC ||
+ mode == SamplePlayerMode::SINGLE_PRESS ||
+ mode == SamplePlayerMode::SINGLE_RETRIG) ||
+ (m_samplePlayerState->isAnyLoopMode() && !running))
+ playStatus = ChannelStatus::OFF;
+ else
+ if (mode == SamplePlayerMode::LOOP_ONCE || mode == SamplePlayerMode::LOOP_ONCE_BAR)
+ playStatus = ChannelStatus::WAIT;
+ }
+ else
+ if (playStatus == ChannelStatus::ENDING)
+ playStatus = ChannelStatus::OFF;
+
+ m_channelState->playStatus.store(playStatus);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleController::advance(Frame bufferSize) const
+{
+ Range<Frame> block(clock::getCurrentFrame(), clock::getCurrentFrame() + bufferSize);
+ m_samplePlayerState->quantizer.advance(block, clock::getQuantizerStep());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleController::press(Frame localFrame, int velocity, bool manual) const
+{
+ ChannelStatus playStatus = m_channelState->playStatus.load();
+ SamplePlayerMode mode = m_samplePlayerState->mode.load();
+ bool isLoop = m_samplePlayerState->isAnyLoopMode();
+
+ switch (playStatus) {
+ case ChannelStatus::OFF:
+ playStatus = pressWhileOff(localFrame, velocity, isLoop, manual); break;
+
+ case ChannelStatus::PLAY:
+ playStatus = pressWhilePlay(localFrame, mode, isLoop, manual); break;
+
+ case ChannelStatus::WAIT:
+ playStatus = ChannelStatus::OFF; break;
+
+ case ChannelStatus::ENDING:
+ playStatus = ChannelStatus::PLAY; break;
+
+ default: break;
+ }
+
+ m_channelState->playStatus.store(playStatus);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleController::release(Frame localFrame) const
+{
+ /* Key release is meaningful only for SINGLE_PRESS modes. */
+
+ if (m_samplePlayerState->mode.load() != SamplePlayerMode::SINGLE_PRESS)
+ return;
+
+ /* Kill it if it's SINGLE_PRESS is playing. Otherwise there might be a
+ quantization step in progress that would play the channel later on:
+ disable it. */
+
+ if (m_channelState->playStatus.load() == ChannelStatus::PLAY)
+ kill(localFrame);
+ else
+ if (m_samplePlayerState->quantizer.isTriggered())
+ m_samplePlayerState->quantizer.clear();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleController::kill(Frame localFrame) const
+{
+ m_channelState->playStatus.store(ChannelStatus::OFF);
+ m_samplePlayerState->tracker.store(m_samplePlayerState->begin.load());
+ m_samplePlayerState->quantizing = false;
+
+ /* Clear data in range [localFrame, (buffer.size)) if the kill event occurs
+ in the middle of the buffer. */
+
+ if (localFrame != 0)
+ m_channelState->buffer.clear(localFrame);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleController::rewind(Frame localFrame) const
+{
+ /* Quantization stops on rewind. */
+
+ m_samplePlayerState->quantizer.clear();
+
+ if (m_channelState->isPlaying()) {
+ m_samplePlayerState->rewinding = true;
+ m_samplePlayerState->offset = localFrame;
+ }
+ else
+ m_samplePlayerState->tracker.store(m_samplePlayerState->begin.load());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+ChannelStatus SampleController::pressWhileOff(Frame localFrame, int velocity, bool isLoop, bool manual) const
+{
+ m_samplePlayerState->offset = localFrame;
+
+ if (isLoop)
+ return ChannelStatus::WAIT;
+
+ if (m_samplePlayerState->velocityAsVol.load() == true)
+ m_channelState->volume_i = u::math::map(velocity, G_MAX_VELOCITY, G_MAX_VOLUME);
+
+ if (clock::canQuantize() && manual) { // manual: don't quantize recorded actions
+ m_samplePlayerState->quantizer.trigger(Q_ACTION_PLAY);
+ return ChannelStatus::OFF;
+ }
+ else
+ return ChannelStatus::PLAY;
+}
+
+
+ChannelStatus SampleController::pressWhilePlay(Frame localFrame, SamplePlayerMode mode, bool isLoop, bool manual) const
+{
+ if (mode == SamplePlayerMode::SINGLE_RETRIG) {
+ if (clock::canQuantize() && manual) // manual: don't quantize recorded actions
+ m_samplePlayerState->quantizer.trigger(Q_ACTION_REWIND);
+ else
+ rewind(localFrame);
+ return ChannelStatus::PLAY;
+ }
+
+ if (isLoop || mode == SamplePlayerMode::SINGLE_ENDLESS)
+ return ChannelStatus::ENDING;
+
+ if (mode == SamplePlayerMode::SINGLE_BASIC) {
+ rewind(localFrame);
+ return ChannelStatus::OFF;
+ }
+
+ return ChannelStatus::OFF;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleController::onBar(Frame localFrame) const
+{
+ G_DEBUG("onBar ch=" << m_channelState->id);
+
+ ChannelStatus playStatus = m_channelState->playStatus.load();
+ SamplePlayerMode mode = m_samplePlayerState->mode.load();
+
+ if (playStatus == ChannelStatus::PLAY && (mode == SamplePlayerMode::LOOP_REPEAT ||
+ mode == SamplePlayerMode::LOOP_ONCE_BAR))
+ rewind(localFrame);
+ else
+ if (playStatus == ChannelStatus::WAIT && mode == SamplePlayerMode::LOOP_ONCE_BAR) {
+ m_channelState->playStatus.store(ChannelStatus::PLAY);
+ m_samplePlayerState->offset = localFrame;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleController::onFirstBeat(Frame localFrame) const
+{
+G_DEBUG("onFirstBeat ch=" << m_channelState->id << ", localFrame=" << localFrame);
+
+ ChannelStatus playStatus = m_channelState->playStatus.load();
+ bool isLoop = m_samplePlayerState->isAnyLoopMode();
+
+ switch (playStatus) {
+
+ case ChannelStatus::PLAY:
+ if (isLoop)
+ rewind(localFrame);
+ break;
+
+ case ChannelStatus::WAIT:
+ m_samplePlayerState->offset = localFrame;
+ m_channelState->playStatus.store(ChannelStatus::PLAY);
+ break;
+
+ case ChannelStatus::ENDING:
+ if (isLoop)
+ kill(localFrame);
+ break;
+
+ default: break;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleController::onStopBySeq() const
+{
+ G_DEBUG("onStopBySeq ch=" << m_channelState->id);
+
+ ChannelStatus playStatus = m_channelState->playStatus.load();
+ bool isReadingActions = m_channelState->readActions.load() == true;
+ bool isLoop = m_samplePlayerState->isAnyLoopMode();
+
+ switch (playStatus) {
+
+ case ChannelStatus::WAIT:
+ /* Loop-mode channels in wait status get stopped right away. */
+ if (isLoop)
+ m_channelState->playStatus.store(ChannelStatus::OFF);
+ break;
+
+ case ChannelStatus::PLAY:
+ /* Kill samples if a) chansStopOnSeqHalt == true (run the sample to end
+ otherwise); b) when a channel is reading (and playing) actions. */
+ if (conf::conf.chansStopOnSeqHalt)
+ if (isLoop || isReadingActions)
+ kill(0);
+ break;
+
+ default: break;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleController::parseAction(const Action& a, Frame localFrame) const
+{
+ bool isLoop = m_samplePlayerState->isAnyLoopMode();
+
+ switch (a.event.getStatus()) {
+ case MidiEvent::NOTE_ON:
+ if (!isLoop)
+ press(localFrame, /*velocity=*/G_MAX_VELOCITY, /*manual=*/false);
+ break;
+ case MidiEvent::NOTE_OFF:
+ if (!isLoop)
+ release(localFrame);
+ break;
+ case MidiEvent::NOTE_KILL:
+ if (!isLoop)
+ kill(localFrame);
+ break;
+ case MidiEvent::ENVELOPE:
+ //calcVolumeEnv_(ch, a); TODO
+ break;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SampleController::toggleReadActions() const
+{
+ ChannelStatus recStatus = m_channelState->recStatus.load();
+ if (clock::isRunning() && recStatus == ChannelStatus::PLAY && !conf::conf.treatRecsAsLoops)
+ kill(0);
+}
+}} // giada::m::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_CHANNEL_SAMPLE_CONTROLLER_H
+#define G_CHANNEL_SAMPLE_CONTROLLER_H
+
+
+#include "core/types.h"
+
+
+namespace giada {
+namespace m
+{
+namespace mixer
+{
+struct Event;
+}
+struct SamplePlayerState;
+class SampleController
+{
+public:
+
+ SampleController(ChannelState*, SamplePlayerState*);
+ SampleController(const SampleController&, ChannelState* c=nullptr, SamplePlayerState* s=nullptr);
+
+ void parse(const mixer::Event& e) const;
+ void onLastFrame() const;
+ void advance(Frame bufferSize) const;
+
+private:
+
+ void press(Frame localFrame, int velocity, bool manual) const;
+ void release(Frame localFrame) const;
+ void kill(Frame localFrame) const;
+ void rewind(Frame localFrame) const;
+
+ ChannelStatus pressWhileOff(Frame localFrame, int velocity, bool isLoop, bool manual) const;
+ ChannelStatus pressWhilePlay(Frame localFrame, SamplePlayerMode mode, bool isLoop, bool manual) const;
+ void toggleReadActions() const;
+
+ void onBar(Frame localFrame) const;
+ void onFirstBeat(Frame localFrame) const;
+ void onStopBySeq() const;
+ void parseAction(const Action& a, Frame localFrame) const;
+
+ ChannelState* m_channelState;
+ SamplePlayerState* m_samplePlayerState;
+};
+}} // giada::m::
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <algorithm>
+#include <cassert>
+#include "core/channels/channel.h"
+#include "core/channels/state.h"
+#include "core/wave.h"
+#include "core/clock.h"
+#include "samplePlayer.h"
+
+
+namespace giada {
+namespace m
+{
+SamplePlayer::SamplePlayer(ChannelState* c)
+: state (std::make_unique<SamplePlayerState>())
+, m_waveId (0)
+, m_sampleController(c, state.get())
+, m_channelState (c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+SamplePlayer::SamplePlayer(const SamplePlayer& o, ChannelState* c)
+: state (std::make_unique<SamplePlayerState>(*o.state))
+, m_waveId (o.m_waveId)
+, m_waveReader (o.m_waveReader)
+, m_sampleController(o.m_sampleController, c, state.get())
+, m_channelState (c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+SamplePlayer::SamplePlayer(const patch::Channel& p, ChannelState* c)
+: state (std::make_unique<SamplePlayerState>(p))
+, m_waveId (p.waveId)
+, m_sampleController(c, state.get())
+, m_channelState (c)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SamplePlayer::parse(const mixer::Event& e) const
+{
+ if (e.type == mixer::EventType::CHANNEL_PITCH)
+ state->pitch.store(e.action.event.getVelocityFloat());
+
+ if (hasWave())
+ m_sampleController.parse(e);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SamplePlayer::advance(Frame bufferSize) const
+{
+ m_sampleController.advance(bufferSize);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SamplePlayer::render(AudioBuffer& /*out*/) const
+{
+ assert(m_channelState != nullptr);
+
+ if (m_waveReader.wave == nullptr || !m_channelState->isPlaying())
+ return;
+
+ Frame begin = state->begin.load();
+ Frame end = state->end.load();
+ Frame tracker = state->tracker.load();
+ float pitch = state->pitch.load();
+ Frame used = 0;
+
+ /* Audio data is temporarily stored to the working audio buffer. */
+
+ AudioBuffer& buffer = m_channelState->buffer;
+
+ /* Adjust tracker in case someone has changed the begin/end points in the
+ meantime. */
+
+ if (tracker < begin || tracker >= end)
+ tracker = begin;
+
+ /* If rewinding, fill the tail first, then reset the tracker to the begin
+ point. The rest is performed as usual. */
+
+ if (state->rewinding) {
+ if (tracker < end)
+ m_waveReader.fill(buffer, tracker, 0, pitch);
+ state->rewinding = false;
+ tracker = begin;
+ }
+
+ used = m_waveReader.fill(buffer, tracker, state->offset, pitch);
+ tracker += used;
+
+G_DEBUG ("block=[" << tracker - used << ", " << tracker << ")" <<
+ ", used=" << used << ", range=[" << begin << ", " << end << ")" <<
+ ", tracker=" << tracker <<
+ ", offset=" << state->offset << ", globalFrame=" << clock::getCurrentFrame());
+
+ if (tracker >= end) {
+G_DEBUG ("last frame tracker=" << tracker);
+ tracker = begin;
+ m_sampleController.onLastFrame();
+ if (shouldLoop()) {
+ Frame offset = std::min(static_cast<Frame>(used / pitch), buffer.countFrames() - 1);
+ tracker += m_waveReader.fill(buffer, tracker, offset, pitch);
+ }
+ }
+
+ state->offset = 0;
+ state->tracker.store(tracker);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SamplePlayer::loadWave(const Wave* w)
+{
+ m_waveReader.wave = w;
+
+ state->tracker.store(0);
+ state->shift.store(0);
+ state->begin.store(0);
+
+ if (w != nullptr) {
+ m_waveId = w->id;
+ m_channelState->playStatus.store(ChannelStatus::OFF);
+ m_channelState->name = w->getBasename(/*ext=*/false);
+ state->end.store(w->getSize() - 1);
+ }
+ else {
+ m_waveId = 0;
+ m_channelState->playStatus.store(ChannelStatus::EMPTY);
+ m_channelState->name = "";
+ state->end.store(0);
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SamplePlayer::setWave(const Wave& w, float samplerateRatio)
+{
+ m_waveReader.wave = &w;
+ m_waveId = w.id;
+
+ if (samplerateRatio != 1.0f) {
+ Frame begin = state->begin.load();
+ Frame end = state->end.load();
+ Frame shift = state->shift.load();
+ state->begin.store(begin * samplerateRatio);
+ state->end.store(end * samplerateRatio);
+ state->shift.store(shift * samplerateRatio);
+ }
+}
+
+
+void SamplePlayer::setInvalidWave()
+{
+ m_waveReader.wave = nullptr;
+ m_waveId = 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void SamplePlayer::kickIn(Frame f)
+{
+ assert(hasWave());
+
+ state->tracker.store(f);
+ m_channelState->playStatus.store(ChannelStatus::PLAY);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool SamplePlayer::shouldLoop() const
+{
+ ChannelStatus playStatus = m_channelState->playStatus.load();
+ SamplePlayerMode mode = state->mode.load();
+
+ return (mode == SamplePlayerMode::LOOP_BASIC ||
+ mode == SamplePlayerMode::LOOP_REPEAT ||
+ mode == SamplePlayerMode::SINGLE_ENDLESS) && playStatus == ChannelStatus::PLAY;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool SamplePlayer::hasWave() const { return m_waveReader.wave != nullptr; }
+bool SamplePlayer::hasLogicalWave() const { return hasWave() && m_waveReader.wave->isLogical(); }
+bool SamplePlayer::hasEditedWave() const { return hasWave() && m_waveReader.wave->isEdited(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+ID SamplePlayer::getWaveId() const
+{
+ return m_waveId;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Frame SamplePlayer::getWaveSize() const
+{
+ return hasWave() ? m_waveReader.wave->getSize() : 0;
+}
+}} // giada::m::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_CHANNEL_SAMPLE_PLAYER_H
+#define G_CHANNEL_SAMPLE_PLAYER_H
+
+
+#include "core/types.h"
+#include "core/const.h"
+#include "core/mixer.h" // TODO - forward declare
+#include "core/audioBuffer.h" // TODO - forward declare
+#include "core/channels/waveReader.h"
+#include "core/channels/sampleController.h"
+
+
+namespace giada {
+namespace m
+{
+class Wave;
+struct SamplePlayerState;
+class SamplePlayer
+{
+public:
+
+ SamplePlayer(ChannelState*);
+ SamplePlayer(const patch::Channel& p, ChannelState*);
+ SamplePlayer(const SamplePlayer&, ChannelState* c=nullptr);
+
+ void parse(const mixer::Event& e) const;
+ void advance(Frame bufferSize) const;
+ void render(AudioBuffer& out) const;
+
+ bool hasWave() const;
+ bool hasLogicalWave() const;
+ bool hasEditedWave() const;
+ ID getWaveId() const;
+ Frame getWaveSize() const;
+
+ /* loadWave
+ Loads Wave 'w' into this channel and sets it up (name, markers, ...). */
+
+ void loadWave(const Wave* w);
+
+ /* setWave
+ Just sets the pointer to a Wave object. Used during de-serialization. The
+ ratio is used to adjust begin/end points in case of patch vs. conf sample
+ rate mismatch. */
+
+ void setWave(const Wave& w, float samplerateRatio);
+
+ /* setInvalidWave
+ Same as setWave(nullptr) plus the invalid ID (i.e. 0). */
+
+ void setInvalidWave();
+
+ /* kickIn
+ Starts the player right away at frame 'f'. Used when launching a loop after
+ being live recorded. */
+
+ void kickIn(Frame f);
+
+
+ /* state
+ Pointer to mutable SamplePlayerState state. */
+
+ std::unique_ptr<SamplePlayerState> state;
+
+private:
+
+ bool shouldLoop() const;
+
+ ID m_waveId;
+
+ /* m_waveReader
+ Used to read data from Wave and fill incoming buffer. */
+
+ WaveReader m_waveReader;
+
+ /* m_sampleController
+ Managers events for this Sample Player. */
+
+ SampleController m_sampleController;
+
+ /* m_channelState
+ Pointer to Channel state. Needed to alter the playStatus status when the
+ sample is over. */
+
+ ChannelState* m_channelState;
+};
+}} // giada::m::
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "core/conf.h"
+#include "core/patch.h"
+#include "state.h"
+
+
+namespace giada {
+namespace m
+{
+MidiLearnerState::MidiLearnerState()
+: enabled (true)
+, filter (0)
+{
+}
+
+
+MidiLearnerState::MidiLearnerState(const patch::Channel& p)
+: enabled (p.midiIn)
+, filter (p.midiInFilter)
+, keyPress (p.midiInKeyPress)
+, keyRelease (p.midiInKeyRel)
+, kill (p.midiInKill)
+, arm (p.midiInArm)
+, volume (p.midiInVolume)
+, mute (p.midiInMute)
+, solo (p.midiInSolo)
+, readActions (p.midiInReadActions)
+, pitch (p.midiInPitch)
+{
+}
+
+
+MidiLearnerState::MidiLearnerState(const MidiLearnerState& o)
+: enabled (o.enabled.load())
+, filter (o.filter.load())
+, keyPress (o.keyPress)
+, keyRelease (o.keyRelease)
+, kill (o.kill)
+, arm (o.arm)
+, volume (o.volume)
+, mute (o.mute)
+, solo (o.solo)
+, readActions (o.readActions)
+, pitch (o.pitch)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool MidiLearnerState::isAllowed(int c) const
+{
+ int filter_ = filter.load();
+ bool enabled_ = enabled.load();
+
+ return enabled_ && (filter_ == -1 || filter_ == c);
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+MidiLighterState::MidiLighterState()
+: enabled(false)
+, playing(0x0)
+{
+}
+
+
+MidiLighterState::MidiLighterState(const patch::Channel& p)
+: enabled(p.midiOutL)
+, playing(p.midiOutLplaying)
+, mute (p.midiOutLmute)
+, solo (p.midiOutLsolo)
+{
+}
+
+
+MidiLighterState::MidiLighterState(const MidiLighterState& o)
+: enabled(o.enabled.load())
+, playing(o.playing)
+, mute (o.mute)
+, solo (o.solo)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+MidiSenderState::MidiSenderState()
+: enabled(false)
+, filter (0)
+{
+}
+
+
+MidiSenderState::MidiSenderState(const patch::Channel& p)
+: enabled(p.midiOut)
+, filter (p.midiOutChan)
+{
+}
+
+
+MidiSenderState::MidiSenderState(const MidiSenderState& o)
+: enabled(o.enabled.load())
+, filter (o.filter.load())
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+MidiReceiverState::MidiReceiverState()
+{
+ midiBuffer.ensureSize(G_DEFAULT_VST_MIDIBUFFER_SIZE);
+}
+
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+SamplePlayerState::SamplePlayerState()
+: tracker (0)
+, pitch (G_DEFAULT_PITCH)
+, mode (SamplePlayerMode::SINGLE_BASIC)
+, velocityAsVol(false)
+, rewinding (false)
+, quantizing (false)
+, offset (0)
+{
+}
+
+
+SamplePlayerState::SamplePlayerState(const SamplePlayerState& o)
+: tracker (o.tracker.load())
+, pitch (o.pitch.load())
+, mode (o.mode.load())
+, shift (o.shift.load())
+, begin (o.begin.load())
+, end (o.end.load())
+, velocityAsVol(o.velocityAsVol.load())
+, rewinding (o.rewinding)
+, quantizing (o.quantizing)
+, offset (o.offset)
+, quantizer (o.quantizer)
+{
+}
+
+
+SamplePlayerState::SamplePlayerState(const patch::Channel& p)
+: tracker (0)
+, pitch (p.pitch)
+, mode (p.mode)
+, shift (p.shift)
+, begin (p.begin)
+, end (p.end)
+, velocityAsVol(p.midiInVeloAsVol)
+, rewinding (false)
+, quantizing (false)
+, offset (0)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool SamplePlayerState::isAnyLoopMode() const
+{
+ SamplePlayerMode m = mode.load();
+
+ return m == SamplePlayerMode::LOOP_BASIC ||
+ m == SamplePlayerMode::LOOP_ONCE ||
+ m == SamplePlayerMode::LOOP_REPEAT ||
+ m == SamplePlayerMode::LOOP_ONCE_BAR;
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+AudioReceiverState::AudioReceiverState(const conf::Conf& c)
+: inputMonitor (c.inputMonitorDefaultOn)
+, overdubProtection(c.overdubProtectionDefaultOn)
+{
+}
+
+
+AudioReceiverState::AudioReceiverState(const patch::Channel& p)
+: inputMonitor (p.inputMonitor)
+, overdubProtection(p.overdubProtection)
+{
+}
+
+
+AudioReceiverState::AudioReceiverState(const AudioReceiverState& o)
+: inputMonitor (o.inputMonitor.load())
+, overdubProtection(o.overdubProtection.load())
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+ChannelState::ChannelState(ID id, Frame bufferSize)
+: id (id)
+, playStatus (ChannelStatus::OFF)
+, recStatus (ChannelStatus::OFF)
+, volume (G_DEFAULT_VOL)
+, pan (G_DEFAULT_PAN)
+, mute (false)
+, solo (false)
+, armed (false)
+, key (0)
+, readActions(true)
+, buffer (bufferSize, G_MAX_IO_CHANS)
+, hasActions (false)
+, height (G_GUI_UNIT)
+, volume_i (1.0f)
+{
+}
+
+
+ChannelState::ChannelState(const ChannelState& o)
+: id (o.id)
+, playStatus (o.playStatus.load())
+, recStatus (o.recStatus.load())
+, volume (o.volume.load())
+, pan (o.pan.load())
+, mute (o.mute.load())
+, solo (o.solo.load())
+, armed (o.armed.load())
+, key (o.key.load())
+, readActions(o.readActions.load())
+, buffer (o.buffer)
+, hasActions (o.hasActions)
+, name (o.name)
+, height (o.height)
+, volume_i (o.volume_i)
+{
+}
+
+
+ChannelState::ChannelState(const patch::Channel& p, Frame bufferSize)
+: id (p.id)
+, playStatus (ChannelStatus::OFF)
+, recStatus (ChannelStatus::OFF)
+, volume (p.volume)
+, pan (p.pan)
+, mute (p.mute)
+, solo (p.solo)
+, armed (p.armed)
+, key (p.key)
+, readActions(p.readActions)
+, buffer (bufferSize, G_MAX_IO_CHANS)
+, hasActions (p.hasActions)
+, name (p.name)
+, height (p.height)
+, volume_i (1.0f)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool ChannelState::isPlaying() const
+{
+ ChannelStatus s = playStatus.load();
+ return s == ChannelStatus::PLAY || s == ChannelStatus::ENDING;
+}
+}} // giada::m::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_CHANNEL_STATE_H
+#define G_CHANNEL_STATE_H
+
+
+#include <string>
+#include <atomic>
+#include "core/const.h"
+#include "core/types.h"
+#include "core/quantizer.h"
+#include "core/audioBuffer.h"
+#include "core/midiLearnParam.h"
+#ifdef WITH_VST
+#include "deps/juce-config.h"
+#endif
+
+
+namespace giada {
+namespace m {
+namespace conf
+{
+struct Conf;
+}
+namespace patch
+{
+struct Channel;
+}
+struct MidiLearnerState
+{
+ MidiLearnerState();
+ MidiLearnerState(const patch::Channel& p);
+ MidiLearnerState(const MidiLearnerState& o);
+
+ /* isAllowed
+ Tells whether the current MIDI channel 'channel' is enabled to receive MIDI
+ data. */
+
+ bool isAllowed(int channel) const;
+
+ /* enabled
+ Tells whether MIDI learning is enabled for the current channel. */
+
+ std::atomic<bool> enabled;
+
+ /* filter
+ Which MIDI channel should be filtered out when receiving MIDI messages.
+ If -1 means 'all'. */
+
+ std::atomic<int> filter;
+
+ /* MIDI learning fields. */
+
+ MidiLearnParam keyPress;
+ MidiLearnParam keyRelease;
+ MidiLearnParam kill;
+ MidiLearnParam arm;
+ MidiLearnParam volume;
+ MidiLearnParam mute;
+ MidiLearnParam solo;
+ MidiLearnParam readActions; // Sample Channels only
+ MidiLearnParam pitch; // Sample Channels only
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+struct MidiLighterState
+{
+ MidiLighterState();
+ MidiLighterState(const patch::Channel& p);
+ MidiLighterState(const MidiLighterState& o);
+
+ /* enabled
+ Tells whether MIDI ligthing is enabled or not. */
+
+ std::atomic<bool> enabled;
+
+ /* MIDI learning fields for MIDI ligthing. */
+
+ MidiLearnParam playing;
+ MidiLearnParam mute;
+ MidiLearnParam solo;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+struct MidiSenderState
+{
+ MidiSenderState();
+ MidiSenderState(const patch::Channel& p);
+ MidiSenderState(const MidiSenderState& o);
+
+ /* enabled
+ Tells whether MIDI output is enabled or not. */
+
+ std::atomic<bool> enabled;
+
+ /* filter
+ Which MIDI channel data should be sent to. */
+
+ std::atomic<int> filter;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+struct MidiReceiverState
+{
+ MidiReceiverState();
+
+ /* midiBuffer
+ Contains MIDI events to be sent to plug-ins. */
+
+ juce::MidiBuffer midiBuffer;
+};
+
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+
+
+struct SamplePlayerState
+{
+ SamplePlayerState();
+ SamplePlayerState(const patch::Channel& p);
+ SamplePlayerState(const SamplePlayerState& o);
+
+ bool isAnyLoopMode() const;
+
+ std::atomic<Frame> tracker;
+ std::atomic<float> pitch;
+ std::atomic<SamplePlayerMode> mode;
+ std::atomic<Frame> shift;
+ std::atomic<Frame> begin;
+ std::atomic<Frame> end;
+
+ /* velocityAsVol
+ Velocity drives volume. */
+
+ std::atomic<bool> velocityAsVol;
+
+ bool rewinding;
+ bool quantizing;
+ Frame offset;
+ Quantizer quantizer;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+struct AudioReceiverState
+{
+ AudioReceiverState(const conf::Conf& c);
+ AudioReceiverState(const patch::Channel& p);
+ AudioReceiverState(const AudioReceiverState& o);
+
+ std::atomic<bool> inputMonitor;
+ std::atomic<bool> overdubProtection;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
+struct ChannelState
+{
+ ChannelState(ID id, Frame bufferSize);
+ ChannelState(const patch::Channel& p, Frame bufferSize);
+ ChannelState(const ChannelState& o);
+
+ bool isPlaying() const;
+
+ ID id;
+
+ std::atomic<ChannelStatus> playStatus;
+ std::atomic<ChannelStatus> recStatus;
+ std::atomic<float> volume;
+ std::atomic<float> pan;
+ std::atomic<bool> mute;
+ std::atomic<bool> solo;
+ std::atomic<bool> armed;
+ std::atomic<int> key;
+ std::atomic<bool> readActions;
+
+ /* buffer (internal)
+ Working buffer for internal processing. */
+
+ AudioBuffer buffer;
+
+ bool hasActions;
+ std::string name;
+ Pixel height;
+
+ /* volume_i (internal)
+ Internal volume used for volume automation and velocity-drives-volume mode
+ on Sample Channels. */
+
+ float volume_i;
+};
+}} // giada::m::
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <memory>
+#include <cassert>
+#include <algorithm>
+#include "core/const.h"
+#include "core/model/model.h"
+#include "core/audioBuffer.h"
+#include "core/wave.h"
+#include "utils/log.h"
+#include "waveReader.h"
+
+
+namespace giada {
+namespace m
+{
+WaveReader::WaveReader()
+: wave (nullptr),
+ m_srcState(nullptr)
+{
+ allocateSrc();
+}
+
+/* -------------------------------------------------------------------------- */
+
+
+WaveReader::WaveReader(const WaveReader& o)
+: wave (o.wave),
+ m_srcState(nullptr)
+{
+ allocateSrc();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+WaveReader::WaveReader(WaveReader&& o)
+: wave (o.wave),
+ m_srcState(nullptr)
+{
+ moveSrc(&o.m_srcState);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+WaveReader& WaveReader::operator=(const WaveReader& o)
+{
+ if (this == &o) return *this;
+ wave = o.wave;
+ allocateSrc();
+ return *this;
+}
+
+
+WaveReader& WaveReader::operator=(WaveReader&& o)
+{
+ if (this == &o) return *this;
+ wave = o.wave;
+ moveSrc(&o.m_srcState);
+ return *this;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+WaveReader::~WaveReader()
+{
+ if (m_srcState != nullptr)
+ src_delete(m_srcState);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Frame WaveReader::fill(AudioBuffer& out, Frame start, Frame offset, float pitch) const
+{
+ assert(wave != nullptr);
+ assert(start >= 0);
+ assert(offset < out.countFrames());
+
+ model::WavesLock l(model::waves); // TODO dependency
+
+ if (pitch == 1.0) return fillCopy(out, start, offset);
+ else return fillResampled(out, start, offset, pitch);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Frame WaveReader::fillResampled(AudioBuffer& dest, Frame start, Frame offset, float pitch) const
+{
+ SRC_DATA srcData;
+
+ srcData.data_in = wave->getFrame(start); // Source data
+ srcData.input_frames = wave->getSize() - start; // How many readable frames
+ srcData.data_out = dest[offset]; // Destination (processed data)
+ srcData.output_frames = dest.countFrames() - offset; // How many frames to process
+ srcData.end_of_input = false;
+ srcData.src_ratio = 1 / pitch;
+
+ src_process(m_srcState, &srcData);
+
+ return srcData.input_frames_used;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Frame WaveReader::fillCopy(AudioBuffer& dest, Frame start, Frame offset) const
+{
+ Frame used = dest.countFrames() - offset;
+ if (used > wave->getSize() - start)
+ used = wave->getSize() - start;
+
+ dest.copyData(wave->getFrame(start), used, G_MAX_IO_CHANS, offset);
+
+ return used;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void WaveReader::allocateSrc()
+{
+ m_srcState = src_new(SRC_LINEAR, G_MAX_IO_CHANS, nullptr);
+ if (m_srcState == nullptr) {
+ u::log::print("[WaveReader] unable to allocate memory for SRC_STATE!\n");
+ throw std::bad_alloc();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void WaveReader::moveSrc(SRC_STATE** other)
+{
+ if (m_srcState != nullptr)
+ src_delete(m_srcState);
+ m_srcState = *other;
+ *other = nullptr;
+}
+}} // giada::m::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_CHANNEL_WAVE_READER_H
+#define G_CHANNEL_WAVE_READER_H
+
+
+#include <samplerate.h>
+#include "core/types.h"
+
+
+namespace giada {
+namespace m
+{
+class Wave;
+class WaveReader final
+{
+public:
+
+ WaveReader();
+ WaveReader(const WaveReader&);
+ WaveReader(WaveReader&&);
+ WaveReader& operator=(const WaveReader&);
+ WaveReader& operator=(WaveReader&&);
+ ~WaveReader();
+
+ Frame fill(AudioBuffer& out, Frame start, Frame offset, float pitch) const;
+
+ /* wave
+ Wave object. Might be null if the channel has no sample. */
+
+ const Wave* wave;
+
+private:
+
+ Frame fillResampled(AudioBuffer& out, Frame start, Frame offset, float pitch) const;
+ Frame fillCopy (AudioBuffer& out, Frame start, Frame offset) const;
+
+ void allocateSrc();
+ void moveSrc(SRC_STATE** o);
+
+ /* srcState
+ Struct from libsamplerate. */
+
+ SRC_STATE* m_srcState;
+};
+}} // giada::m::
+
+
+#endif
#include <atomic>
#include <cassert>
#include "glue/main.h"
-#include "utils/math.h"
+#include "glue/events.h"
#include "core/model/model.h"
#include "core/conf.h"
+#include "core/sequencer.h"
#include "core/const.h"
#include "core/kernelAudio.h"
#include "core/mixerHandler.h"
#include "core/kernelMidi.h"
+#include "utils/math.h"
#include "clock.h"
-namespace giada {
-namespace m {
-namespace clock
+namespace giada::m::clock
{
namespace
{
std::atomic<int> currentFrame_(0);
std::atomic<int> currentBeat_(0);
-int quanto_ = 1; // Quantizer step
+/* quantizerStep_
+Tells how many frames to wait to perform a quantized action. */
+
+int quantizerStep_ = 1;
+
+/* midiTC*
+MIDI timecode variables. */
int midiTCrate_ = 0; // Send MTC data every midiTCrate_ frames
int midiTCframes_ = 0;
int midiTCminutes_ = 0;
int midiTChours_ = 0;
-
-#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
+#ifdef WITH_AUDIO_JACK
kernelAudio::JackState jackStatePrev_;
#endif
void recomputeFrames_(model::Clock& c)
{
- c.framesInLoop = (conf::conf.samplerate * (60.0f / c.bpm)) * c.beats;
- c.framesInBar = c.framesInLoop / (float) c.bars;
- c.framesInBeat = c.framesInLoop / (float) c.beats;
+ c.framesInLoop = static_cast<int>((conf::conf.samplerate * (60.0f / c.bpm)) * c.beats);
+ c.framesInBar = static_cast<int>(c.framesInLoop / (float) c.bars);
+ c.framesInBeat = static_cast<int>(c.framesInLoop / (float) c.beats);
c.framesInSeq = c.framesInBeat * G_MAX_BEATS;
if (c.quantize != 0)
- quanto_ = c.framesInBeat / c.quantize;
+ quantizerStep_ = c.framesInBeat / c.quantize;
}
-}; // {anonymous}
+} // {anonymous}
/* -------------------------------------------------------------------------- */
void init(int sampleRate, float midiTCfps)
{
- midiTCrate_ = (sampleRate / midiTCfps) * G_MAX_IO_CHANS; // stereo values
+ midiTCrate_ = static_cast<int>((sampleRate / midiTCfps) * G_MAX_IO_CHANS); // stereo values
model::onSwap(model::clock, [&](model::Clock& c)
{
bool quantoHasPassed()
{
- return currentFrame_.load() % quanto_ == 0;
+ return clock::getQuantizerValue() != 0 && currentFrame_.load() % quantizerStep_ == 0;
}
void setBpm(float b)
{
-#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
-
- /* Can't change bpm from within Giada when using JACK. */
-
- if (m::kernelAudio::getAPI() == G_SYS_API_JACK)
- return;
-
-#endif
-
- b = u::math::bound(b, G_MIN_BPM, G_MAX_BPM);
+ b = std::clamp(b, G_MIN_BPM, G_MAX_BPM);
model::onSwap(model::clock, [&](model::Clock& c)
{
void setBeats(int newBeats, int newBars)
{
- newBeats = u::math::bound(newBeats, 1, G_MAX_BEATS);
- newBars = u::math::bound(newBars, 1, newBeats); // Bars cannot be greater than beats
+ newBeats = std::clamp(newBeats, 1, G_MAX_BEATS);
+ newBars = std::clamp(newBars, 1, newBeats); // Bars cannot be greater than beats
model::onSwap(model::clock, [&](model::Clock& c)
{
if (c->status == ClockStatus::WAITING) {
int f = currentFrameWait_.load() + 1;
- if (f >= c->framesInLoop)
- f = 0;
+ f %= c->framesInLoop;
currentFrameWait_.store(f);
return;
}
int f = currentFrame_.load() + 1;
int b = currentBeat_.load();
- if (f >= c->framesInLoop) {
- f = 0;
- b = 0;
- }
- else
- if (f % c->framesInBeat == 0) // If is on beat
- b++;
+ f %= c->framesInLoop;
+ b = f / c->framesInBeat;
currentFrame_.store(f);
currentBeat_.store(b);
kernelMidi::send(0x00, 0x00, 0x00); // mins, secs, frames 0
kernelMidi::send(MIDI_EOX, -1, -1); // end of sysex
}
+ else
+ if (conf::conf.midiSync == MIDI_SYNC_CLOCK_M)
+ kernelMidi::send(MIDI_POSITION_PTR, 0, 0);
}
/* -------------------------------------------------------------------------- */
-#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
+#ifdef WITH_AUDIO_JACK
void recvJackSync()
{
/* TODO - these things should be processed by a higher level,
above clock:: ----> clockManager */
- kernelAudio::JackState jackState = kernelAudio::jackTransportQuery();
+ kernelAudio::JackState jackStateCurr = kernelAudio::jackTransportQuery();
- if (jackState.running != jackStatePrev_.running) {
- if (jackState.running) {
- if (!isRunning())
- mh::startSequencer();
+ if (jackStateCurr != jackStatePrev_) {
+
+ if (jackStateCurr.frame != jackStatePrev_.frame && jackStateCurr.frame == 0) {
+G_DEBUG("JackState received - rewind to frame 0");
+ sequencer::rewind();
}
- else {
- if (isRunning())
- mh::stopSequencer();
+
+ if (jackStateCurr.bpm != jackStatePrev_.bpm && jackStateCurr.bpm > 1.0f) { // 0 bpm if Jack does not send that info
+G_DEBUG("JackState received - bpm=" << jackStateCurr.bpm);
+ c::main::setBpm(jackStateCurr.bpm);
}
- }
- if (jackState.bpm != jackStatePrev_.bpm)
- if (jackState.bpm > 1.0f) // 0 bpm if Jack does not send that info
- c::main::setBpm(jackState.bpm);
- if (jackState.frame == 0 && jackState.frame != jackStatePrev_.frame)
- mh::rewindSequencer();
+ if (jackStateCurr.running != jackStatePrev_.running) {
+G_DEBUG("JackState received - running=" << jackStateCurr.running);
+ jackStateCurr.running ? sequencer::start() : sequencer::stop();
+ }
+ }
- jackStatePrev_ = jackState;
+ jackStatePrev_ = jackStateCurr;
}
#endif
model::ClockLock lock(model::clock);
const model::Clock* c = model::clock.get();
-
return c->quantize > 0 && c->status == ClockStatus::RUNNING;
}
/* -------------------------------------------------------------------------- */
-int getCurrentFrame() { return currentFrame_.load(); }
-int getCurrentBeat() { return currentBeat_.load(); }
-int getQuanto() { return quanto_; }
-ClockStatus getStatus() { model::ClockLock lock(model::clock); return model::clock.get()->status; }
-int getFramesInLoop() { model::ClockLock lock(model::clock); return model::clock.get()->framesInLoop; }
-int getFramesInBar() { model::ClockLock lock(model::clock); return model::clock.get()->framesInBar; }
-int getFramesInBeat() { model::ClockLock lock(model::clock); return model::clock.get()->framesInBeat; }
-int getFramesInSeq() { model::ClockLock lock(model::clock); return model::clock.get()->framesInSeq; }
-int getQuantize() { model::ClockLock lock(model::clock); return model::clock.get()->quantize; }
-float getBpm() { model::ClockLock lock(model::clock); return model::clock.get()->bpm; }
-int getBeats() { model::ClockLock lock(model::clock); return model::clock.get()->beats; }
-int getBars() { model::ClockLock lock(model::clock); return model::clock.get()->bars; }
-}}}; // giada::m::clock::
+Frame quantize(Frame f)
+{
+ if (!canQuantize()) return f;
+ return u::math::quantize(f, quantizerStep_) % getFramesInLoop(); // No overflow
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int getCurrentFrame() { return currentFrame_.load(); }
+int getCurrentBeat() { return currentBeat_.load(); }
+int getQuantizerStep() { return quantizerStep_; }
+ClockStatus getStatus() { model::ClockLock lock(model::clock); return model::clock.get()->status; }
+int getFramesInLoop() { model::ClockLock lock(model::clock); return model::clock.get()->framesInLoop; }
+int getFramesInBar() { model::ClockLock lock(model::clock); return model::clock.get()->framesInBar; }
+int getFramesInBeat() { model::ClockLock lock(model::clock); return model::clock.get()->framesInBeat; }
+int getFramesInSeq() { model::ClockLock lock(model::clock); return model::clock.get()->framesInSeq; }
+int getQuantizerValue() { model::ClockLock lock(model::clock); return model::clock.get()->quantize; }
+float getBpm() { model::ClockLock lock(model::clock); return model::clock.get()->bpm; }
+int getBeats() { model::ClockLock lock(model::clock); return model::clock.get()->beats; }
+int getBars() { model::ClockLock lock(model::clock); return model::clock.get()->bars; }
+} // giada::m::clock::
#include "types.h"
-namespace giada {
-namespace m {
-namespace clock
+namespace giada::m::clock
{
void init(int sampleRate, float midiTCfps);
void sendMIDIrewind();
-#if defined(__linux__) || defined(__FreeBSD__)
+#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD) || defined(G_OS_MAC)
void recvJackSync();
#endif
int getFramesInBeat();
int getFramesInLoop();
int getFramesInSeq();
-int getQuantize();
-int getQuanto();
+int getQuantizerValue();
+int getQuantizerStep();
ClockStatus getStatus();
/* incrCurrentFrame
-Increases current frame of a single step (+1). */
+Increases current frame by a single step (+1). */
void incrCurrentFrame();
/* quantoHasPassed
-Tells whether a quanto unit has passed yet. */
+Tells whether a quantizer unit has passed yet. */
bool quantoHasPassed();
-/* quantoHasPassed
-Whether the quantizer value is > 0 and the clock is running. */
+/* canQuantize
+Tells whether the quantizer value is > 0 and the clock is running. */
bool canQuantize();
+/* quantize
+Quantizes the global frame 'f'. */
+
+Frame quantize(Frame f);
+
void setBpm(float b);
void setBeats(int beats, int bars);
void setQuantize(int q);
void rewind();
void setStatus(ClockStatus s);
-}}}; // giada::m::clock::
+} // giada::m::clock::
#endif
/* -------------------------------------------------------------------------- */
+void sanitize_()
+{
+ conf.soundDeviceOut = std::max(0, conf.soundDeviceOut);
+ conf.channelsOut = std::max(0, conf.channelsOut);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
/* createConfigFolder
Creates local folder where to put the configuration file. Path differs from OS
to OS. */
#endif
}
-
-}; // {anonymous}
+} // {anonymous}
/* -------------------------------------------------------------------------- */
nl::json j = nl::json::parse(ifs);
- conf.logMode = j.value(CONF_KEY_LOG_MODE, conf.logMode);
- conf.soundSystem = j.value(CONF_KEY_SOUND_SYSTEM, conf.soundSystem);
- conf.soundDeviceOut = j.value(CONF_KEY_SOUND_DEVICE_OUT, conf.soundDeviceOut);
- conf.soundDeviceIn = j.value(CONF_KEY_SOUND_DEVICE_IN, conf.soundDeviceIn);
- conf.channelsOut = j.value(CONF_KEY_CHANNELS_OUT, conf.channelsOut);
- conf.channelsIn = j.value(CONF_KEY_CHANNELS_IN, conf.channelsIn);
- conf.samplerate = j.value(CONF_KEY_SAMPLERATE, conf.samplerate);
- conf.buffersize = j.value(CONF_KEY_BUFFER_SIZE, conf.buffersize);
- conf.limitOutput = j.value(CONF_KEY_LIMIT_OUTPUT, conf.limitOutput);
- conf.rsmpQuality = j.value(CONF_KEY_RESAMPLE_QUALITY, conf.rsmpQuality);
- conf.midiSystem = j.value(CONF_KEY_MIDI_SYSTEM, conf.midiSystem);
- conf.midiPortOut = j.value(CONF_KEY_MIDI_PORT_OUT, conf.midiPortOut);
- conf.midiPortIn = j.value(CONF_KEY_MIDI_PORT_IN, conf.midiPortIn);
- conf.midiMapPath = j.value(CONF_KEY_MIDIMAP_PATH, conf.midiMapPath);
- conf.lastFileMap = j.value(CONF_KEY_LAST_MIDIMAP, conf.lastFileMap);
- conf.midiSync = j.value(CONF_KEY_MIDI_SYNC, conf.midiSync);
- conf.midiTCfps = j.value(CONF_KEY_MIDI_TC_FPS, conf.midiTCfps);
- conf.recsStopOnChanHalt = j.value(CONF_KEY_RECS_STOP_ON_CHAN_HALT, conf.recsStopOnChanHalt);
- conf.chansStopOnSeqHalt = j.value(CONF_KEY_CHANS_STOP_ON_SEQ_HALT, conf.chansStopOnSeqHalt);
- conf.treatRecsAsLoops = j.value(CONF_KEY_TREAT_RECS_AS_LOOPS, conf.treatRecsAsLoops);
- conf.inputMonitorDefaultOn = j.value(CONF_KEY_INPUT_MONITOR_DEFAULT_ON, conf.inputMonitorDefaultOn);
- conf.pluginPath = j.value(CONF_KEY_PLUGINS_PATH, conf.pluginPath);
- conf.patchPath = j.value(CONF_KEY_PATCHES_PATH, conf.patchPath);
- conf.samplePath = j.value(CONF_KEY_SAMPLES_PATH, conf.samplePath);
- conf.mainWindowX = j.value(CONF_KEY_MAIN_WINDOW_X, conf.mainWindowX);
- conf.mainWindowY = j.value(CONF_KEY_MAIN_WINDOW_Y, conf.mainWindowY);
- conf.mainWindowW = j.value(CONF_KEY_MAIN_WINDOW_W, conf.mainWindowW);
- conf.mainWindowH = j.value(CONF_KEY_MAIN_WINDOW_H, conf.mainWindowH);
- conf.browserX = j.value(CONF_KEY_BROWSER_X, conf.browserX);
- conf.browserY = j.value(CONF_KEY_BROWSER_Y, conf.browserY);
- conf.browserW = j.value(CONF_KEY_BROWSER_W, conf.browserW);
- conf.browserH = j.value(CONF_KEY_BROWSER_H, conf.browserH);
- conf.browserPosition = j.value(CONF_KEY_BROWSER_POSITION, conf.browserPosition);
- conf.browserLastPath = j.value(CONF_KEY_BROWSER_LAST_PATH, conf.browserLastPath);
- conf.browserLastValue = j.value(CONF_KEY_BROWSER_LAST_VALUE, conf.browserLastValue);
- conf.actionEditorX = j.value(CONF_KEY_ACTION_EDITOR_X, conf.actionEditorX);
- conf.actionEditorY = j.value(CONF_KEY_ACTION_EDITOR_Y, conf.actionEditorY);
- conf.actionEditorW = j.value(CONF_KEY_ACTION_EDITOR_W, conf.actionEditorW);
- conf.actionEditorH = j.value(CONF_KEY_ACTION_EDITOR_H, conf.actionEditorH);
- conf.actionEditorZoom = j.value(CONF_KEY_ACTION_EDITOR_ZOOM, conf.actionEditorZoom);
- conf.actionEditorGridVal = j.value(CONF_KEY_ACTION_EDITOR_GRID_VAL, conf.actionEditorGridVal);
- conf.actionEditorGridOn = j.value(CONF_KEY_ACTION_EDITOR_GRID_ON, conf.actionEditorGridOn);
- conf.sampleEditorX = j.value(CONF_KEY_SAMPLE_EDITOR_X, conf.sampleEditorX);
- conf.sampleEditorY = j.value(CONF_KEY_SAMPLE_EDITOR_Y, conf.sampleEditorY);
- conf.sampleEditorW = j.value(CONF_KEY_SAMPLE_EDITOR_W, conf.sampleEditorW);
- conf.sampleEditorH = j.value(CONF_KEY_SAMPLE_EDITOR_H, conf.sampleEditorH);
- conf.sampleEditorGridVal = j.value(CONF_KEY_SAMPLE_EDITOR_GRID_VAL, conf.sampleEditorGridVal);
- conf.sampleEditorGridOn = j.value(CONF_KEY_SAMPLE_EDITOR_GRID_ON, conf.sampleEditorGridOn);
- conf.pianoRollY = j.value(CONF_KEY_PIANO_ROLL_Y, conf.pianoRollY);
- conf.pianoRollH = j.value(CONF_KEY_PIANO_ROLL_H, conf.pianoRollH);
- conf.sampleActionEditorH = j.value(CONF_KEY_SAMPLE_ACTION_EDITOR_H, conf.sampleActionEditorH);
- conf.velocityEditorH = j.value(CONF_KEY_VELOCITY_EDITOR_H, conf.velocityEditorH);
- conf.envelopeEditorH = j.value(CONF_KEY_ENVELOPE_EDITOR_H, conf.envelopeEditorH);
- conf.pluginListX = j.value(CONF_KEY_PLUGIN_LIST_X, conf.pluginListX);
- conf.pluginListY = j.value(CONF_KEY_PLUGIN_LIST_Y, conf.pluginListY);
- conf.midiInputX = j.value(CONF_KEY_MIDI_INPUT_X, conf.midiInputX);
- conf.midiInputY = j.value(CONF_KEY_MIDI_INPUT_Y, conf.midiInputY);
- conf.midiInputW = j.value(CONF_KEY_MIDI_INPUT_W, conf.midiInputW);
- conf.midiInputH = j.value(CONF_KEY_MIDI_INPUT_H, conf.midiInputH);
- conf.recTriggerMode = j.value(CONF_KEY_REC_TRIGGER_MODE, conf.recTriggerMode);
- conf.recTriggerLevel = j.value(CONF_KEY_REC_TRIGGER_LEVEL, conf.recTriggerLevel);
- conf.midiInEnabled = j.value(CONF_KEY_MIDI_IN, conf.midiInEnabled);
- conf.midiInFilter = j.value(CONF_KEY_MIDI_IN_FILTER, conf.midiInFilter);
- conf.midiInRewind = j.value(CONF_KEY_MIDI_IN_REWIND, conf.midiInRewind);
- conf.midiInStartStop = j.value(CONF_KEY_MIDI_IN_START_STOP, conf.midiInStartStop);
- conf.midiInActionRec = j.value(CONF_KEY_MIDI_IN_ACTION_REC, conf.midiInActionRec);
- conf.midiInInputRec = j.value(CONF_KEY_MIDI_IN_INPUT_REC, conf.midiInInputRec);
- conf.midiInMetronome = j.value(CONF_KEY_MIDI_IN_METRONOME, conf.midiInMetronome);
- conf.midiInVolumeIn = j.value(CONF_KEY_MIDI_IN_VOLUME_IN, conf.midiInVolumeIn);
- conf.midiInVolumeOut = j.value(CONF_KEY_MIDI_IN_VOLUME_OUT, conf.midiInVolumeOut);
- conf.midiInBeatDouble = j.value(CONF_KEY_MIDI_IN_BEAT_DOUBLE, conf.midiInBeatDouble);
- conf.midiInBeatHalf = j.value(CONF_KEY_MIDI_IN_BEAT_HALF, conf.midiInBeatHalf);
+ conf.logMode = j.value(CONF_KEY_LOG_MODE, conf.logMode);
+ conf.soundSystem = j.value(CONF_KEY_SOUND_SYSTEM, conf.soundSystem);
+ conf.soundDeviceOut = j.value(CONF_KEY_SOUND_DEVICE_OUT, conf.soundDeviceOut);
+ conf.soundDeviceIn = j.value(CONF_KEY_SOUND_DEVICE_IN, conf.soundDeviceIn);
+ conf.channelsOut = j.value(CONF_KEY_CHANNELS_OUT, conf.channelsOut);
+ conf.channelsInCount = j.value(CONF_KEY_CHANNELS_IN_COUNT, conf.channelsInCount);
+ conf.channelsInStart = j.value(CONF_KEY_CHANNELS_IN_START, conf.channelsInStart);
+ conf.samplerate = j.value(CONF_KEY_SAMPLERATE, conf.samplerate);
+ conf.buffersize = j.value(CONF_KEY_BUFFER_SIZE, conf.buffersize);
+ conf.limitOutput = j.value(CONF_KEY_LIMIT_OUTPUT, conf.limitOutput);
+ conf.rsmpQuality = j.value(CONF_KEY_RESAMPLE_QUALITY, conf.rsmpQuality);
+ conf.midiSystem = j.value(CONF_KEY_MIDI_SYSTEM, conf.midiSystem);
+ conf.midiPortOut = j.value(CONF_KEY_MIDI_PORT_OUT, conf.midiPortOut);
+ conf.midiPortIn = j.value(CONF_KEY_MIDI_PORT_IN, conf.midiPortIn);
+ conf.midiMapPath = j.value(CONF_KEY_MIDIMAP_PATH, conf.midiMapPath);
+ conf.lastFileMap = j.value(CONF_KEY_LAST_MIDIMAP, conf.lastFileMap);
+ conf.midiSync = j.value(CONF_KEY_MIDI_SYNC, conf.midiSync);
+ conf.midiTCfps = j.value(CONF_KEY_MIDI_TC_FPS, conf.midiTCfps);
+ conf.chansStopOnSeqHalt = j.value(CONF_KEY_CHANS_STOP_ON_SEQ_HALT, conf.chansStopOnSeqHalt);
+ conf.treatRecsAsLoops = j.value(CONF_KEY_TREAT_RECS_AS_LOOPS, conf.treatRecsAsLoops);
+ conf.inputMonitorDefaultOn = j.value(CONF_KEY_INPUT_MONITOR_DEFAULT_ON, conf.inputMonitorDefaultOn);
+ conf.overdubProtectionDefaultOn = j.value(CONF_KEY_OVERDUB_PROTECTION_DEFAULT_ON, conf.overdubProtectionDefaultOn);
+ conf.pluginPath = j.value(CONF_KEY_PLUGINS_PATH, conf.pluginPath);
+ conf.patchPath = j.value(CONF_KEY_PATCHES_PATH, conf.patchPath);
+ conf.samplePath = j.value(CONF_KEY_SAMPLES_PATH, conf.samplePath);
+ conf.mainWindowX = j.value(CONF_KEY_MAIN_WINDOW_X, conf.mainWindowX);
+ conf.mainWindowY = j.value(CONF_KEY_MAIN_WINDOW_Y, conf.mainWindowY);
+ conf.mainWindowW = j.value(CONF_KEY_MAIN_WINDOW_W, conf.mainWindowW);
+ conf.mainWindowH = j.value(CONF_KEY_MAIN_WINDOW_H, conf.mainWindowH);
+ conf.browserX = j.value(CONF_KEY_BROWSER_X, conf.browserX);
+ conf.browserY = j.value(CONF_KEY_BROWSER_Y, conf.browserY);
+ conf.browserW = j.value(CONF_KEY_BROWSER_W, conf.browserW);
+ conf.browserH = j.value(CONF_KEY_BROWSER_H, conf.browserH);
+ conf.browserPosition = j.value(CONF_KEY_BROWSER_POSITION, conf.browserPosition);
+ conf.browserLastPath = j.value(CONF_KEY_BROWSER_LAST_PATH, conf.browserLastPath);
+ conf.browserLastValue = j.value(CONF_KEY_BROWSER_LAST_VALUE, conf.browserLastValue);
+ conf.actionEditorX = j.value(CONF_KEY_ACTION_EDITOR_X, conf.actionEditorX);
+ conf.actionEditorY = j.value(CONF_KEY_ACTION_EDITOR_Y, conf.actionEditorY);
+ conf.actionEditorW = j.value(CONF_KEY_ACTION_EDITOR_W, conf.actionEditorW);
+ conf.actionEditorH = j.value(CONF_KEY_ACTION_EDITOR_H, conf.actionEditorH);
+ conf.actionEditorZoom = j.value(CONF_KEY_ACTION_EDITOR_ZOOM, conf.actionEditorZoom);
+ conf.actionEditorGridVal = j.value(CONF_KEY_ACTION_EDITOR_GRID_VAL, conf.actionEditorGridVal);
+ conf.actionEditorGridOn = j.value(CONF_KEY_ACTION_EDITOR_GRID_ON, conf.actionEditorGridOn);
+ conf.sampleEditorX = j.value(CONF_KEY_SAMPLE_EDITOR_X, conf.sampleEditorX);
+ conf.sampleEditorY = j.value(CONF_KEY_SAMPLE_EDITOR_Y, conf.sampleEditorY);
+ conf.sampleEditorW = j.value(CONF_KEY_SAMPLE_EDITOR_W, conf.sampleEditorW);
+ conf.sampleEditorH = j.value(CONF_KEY_SAMPLE_EDITOR_H, conf.sampleEditorH);
+ conf.sampleEditorGridVal = j.value(CONF_KEY_SAMPLE_EDITOR_GRID_VAL, conf.sampleEditorGridVal);
+ conf.sampleEditorGridOn = j.value(CONF_KEY_SAMPLE_EDITOR_GRID_ON, conf.sampleEditorGridOn);
+ conf.pianoRollY = j.value(CONF_KEY_PIANO_ROLL_Y, conf.pianoRollY);
+ conf.pianoRollH = j.value(CONF_KEY_PIANO_ROLL_H, conf.pianoRollH);
+ conf.sampleActionEditorH = j.value(CONF_KEY_SAMPLE_ACTION_EDITOR_H, conf.sampleActionEditorH);
+ conf.velocityEditorH = j.value(CONF_KEY_VELOCITY_EDITOR_H, conf.velocityEditorH);
+ conf.envelopeEditorH = j.value(CONF_KEY_ENVELOPE_EDITOR_H, conf.envelopeEditorH);
+ conf.pluginListX = j.value(CONF_KEY_PLUGIN_LIST_X, conf.pluginListX);
+ conf.pluginListY = j.value(CONF_KEY_PLUGIN_LIST_Y, conf.pluginListY);
+ conf.midiInputX = j.value(CONF_KEY_MIDI_INPUT_X, conf.midiInputX);
+ conf.midiInputY = j.value(CONF_KEY_MIDI_INPUT_Y, conf.midiInputY);
+ conf.midiInputW = j.value(CONF_KEY_MIDI_INPUT_W, conf.midiInputW);
+ conf.midiInputH = j.value(CONF_KEY_MIDI_INPUT_H, conf.midiInputH);
+ conf.recTriggerMode = j.value(CONF_KEY_REC_TRIGGER_MODE, conf.recTriggerMode);
+ conf.recTriggerLevel = j.value(CONF_KEY_REC_TRIGGER_LEVEL, conf.recTriggerLevel);
+ conf.midiInEnabled = j.value(CONF_KEY_MIDI_IN, conf.midiInEnabled);
+ conf.midiInFilter = j.value(CONF_KEY_MIDI_IN_FILTER, conf.midiInFilter);
+ conf.midiInRewind = j.value(CONF_KEY_MIDI_IN_REWIND, conf.midiInRewind);
+ conf.midiInStartStop = j.value(CONF_KEY_MIDI_IN_START_STOP, conf.midiInStartStop);
+ conf.midiInActionRec = j.value(CONF_KEY_MIDI_IN_ACTION_REC, conf.midiInActionRec);
+ conf.midiInInputRec = j.value(CONF_KEY_MIDI_IN_INPUT_REC, conf.midiInInputRec);
+ conf.midiInMetronome = j.value(CONF_KEY_MIDI_IN_METRONOME, conf.midiInMetronome);
+ conf.midiInVolumeIn = j.value(CONF_KEY_MIDI_IN_VOLUME_IN, conf.midiInVolumeIn);
+ conf.midiInVolumeOut = j.value(CONF_KEY_MIDI_IN_VOLUME_OUT, conf.midiInVolumeOut);
+ conf.midiInBeatDouble = j.value(CONF_KEY_MIDI_IN_BEAT_DOUBLE, conf.midiInBeatDouble);
+ conf.midiInBeatHalf = j.value(CONF_KEY_MIDI_IN_BEAT_HALF, conf.midiInBeatHalf);
#ifdef WITH_VST
- conf.pluginChooserX = j.value(CONF_KEY_PLUGIN_CHOOSER_X, conf.pluginChooserX);
- conf.pluginChooserY = j.value(CONF_KEY_PLUGIN_CHOOSER_Y, conf.pluginChooserY);
- conf.pluginChooserW = j.value(CONF_KEY_PLUGIN_CHOOSER_W, conf.pluginChooserW);
- conf.pluginChooserH = j.value(CONF_KEY_PLUGIN_CHOOSER_H, conf.pluginChooserH);
- conf.pluginSortMethod = j.value(CONF_KEY_PLUGIN_SORT_METHOD, conf.pluginSortMethod);
+ conf.pluginChooserX = j.value(CONF_KEY_PLUGIN_CHOOSER_X, conf.pluginChooserX);
+ conf.pluginChooserY = j.value(CONF_KEY_PLUGIN_CHOOSER_Y, conf.pluginChooserY);
+ conf.pluginChooserW = j.value(CONF_KEY_PLUGIN_CHOOSER_W, conf.pluginChooserW);
+ conf.pluginChooserH = j.value(CONF_KEY_PLUGIN_CHOOSER_H, conf.pluginChooserH);
+ conf.pluginSortMethod = j.value(CONF_KEY_PLUGIN_SORT_METHOD, conf.pluginSortMethod);
#endif
+ sanitize_();
+
return true;
}
nl::json j;
- j[CONF_KEY_HEADER] = "GIADACFG";
- j[CONF_KEY_LOG_MODE] = conf.logMode;
- j[CONF_KEY_SOUND_SYSTEM] = conf.soundSystem;
- j[CONF_KEY_SOUND_DEVICE_OUT] = conf.soundDeviceOut;
- j[CONF_KEY_SOUND_DEVICE_IN] = conf.soundDeviceIn;
- j[CONF_KEY_CHANNELS_OUT] = conf.channelsOut;
- j[CONF_KEY_CHANNELS_IN] = conf.channelsIn;
- j[CONF_KEY_SAMPLERATE] = conf.samplerate;
- j[CONF_KEY_BUFFER_SIZE] = conf.buffersize;
- j[CONF_KEY_LIMIT_OUTPUT] = conf.limitOutput;
- j[CONF_KEY_RESAMPLE_QUALITY] = conf.rsmpQuality;
- j[CONF_KEY_MIDI_SYSTEM] = conf.midiSystem;
- j[CONF_KEY_MIDI_PORT_OUT] = conf.midiPortOut;
- j[CONF_KEY_MIDI_PORT_IN] = conf.midiPortIn;
- j[CONF_KEY_MIDIMAP_PATH] = conf.midiMapPath;
- j[CONF_KEY_LAST_MIDIMAP] = conf.lastFileMap;
- j[CONF_KEY_MIDI_SYNC] = conf.midiSync;
- j[CONF_KEY_MIDI_TC_FPS] = conf.midiTCfps;
- j[CONF_KEY_MIDI_IN] = conf.midiInEnabled;
- j[CONF_KEY_MIDI_IN_FILTER] = conf.midiInFilter;
- j[CONF_KEY_MIDI_IN_REWIND] = conf.midiInRewind;
- j[CONF_KEY_MIDI_IN_START_STOP] = conf.midiInStartStop;
- j[CONF_KEY_MIDI_IN_ACTION_REC] = conf.midiInActionRec;
- j[CONF_KEY_MIDI_IN_INPUT_REC] = conf.midiInInputRec;
- j[CONF_KEY_MIDI_IN_METRONOME] = conf.midiInMetronome;
- j[CONF_KEY_MIDI_IN_VOLUME_IN] = conf.midiInVolumeIn;
- j[CONF_KEY_MIDI_IN_VOLUME_OUT] = conf.midiInVolumeOut;
- j[CONF_KEY_MIDI_IN_BEAT_DOUBLE] = conf.midiInBeatDouble;
- j[CONF_KEY_MIDI_IN_BEAT_HALF] = conf.midiInBeatHalf;
- j[CONF_KEY_RECS_STOP_ON_CHAN_HALT] = conf.recsStopOnChanHalt;
- j[CONF_KEY_CHANS_STOP_ON_SEQ_HALT] = conf.chansStopOnSeqHalt;
- j[CONF_KEY_TREAT_RECS_AS_LOOPS] = conf.treatRecsAsLoops;
- j[CONF_KEY_INPUT_MONITOR_DEFAULT_ON] = conf.inputMonitorDefaultOn;
- j[CONF_KEY_PLUGINS_PATH] = conf.pluginPath;
- j[CONF_KEY_PATCHES_PATH] = conf.patchPath;
- j[CONF_KEY_SAMPLES_PATH] = conf.samplePath;
- j[CONF_KEY_MAIN_WINDOW_X] = conf.mainWindowX;
- j[CONF_KEY_MAIN_WINDOW_Y] = conf.mainWindowY;
- j[CONF_KEY_MAIN_WINDOW_W] = conf.mainWindowW;
- j[CONF_KEY_MAIN_WINDOW_H] = conf.mainWindowH;
- j[CONF_KEY_BROWSER_X] = conf.browserX;
- j[CONF_KEY_BROWSER_Y] = conf.browserY;
- j[CONF_KEY_BROWSER_W] = conf.browserW;
- j[CONF_KEY_BROWSER_H] = conf.browserH;
- j[CONF_KEY_BROWSER_POSITION] = conf.browserPosition;
- j[CONF_KEY_BROWSER_LAST_PATH] = conf.browserLastPath;
- j[CONF_KEY_BROWSER_LAST_VALUE] = conf.browserLastValue;
- j[CONF_KEY_ACTION_EDITOR_X] = conf.actionEditorX;
- j[CONF_KEY_ACTION_EDITOR_Y] = conf.actionEditorY;
- j[CONF_KEY_ACTION_EDITOR_W] = conf.actionEditorW;
- j[CONF_KEY_ACTION_EDITOR_H] = conf.actionEditorH;
- j[CONF_KEY_ACTION_EDITOR_ZOOM] = conf.actionEditorZoom;
- j[CONF_KEY_ACTION_EDITOR_GRID_VAL] = conf.actionEditorGridVal;
- j[CONF_KEY_ACTION_EDITOR_GRID_ON] = conf.actionEditorGridOn;
- j[CONF_KEY_SAMPLE_EDITOR_X] = conf.sampleEditorX;
- j[CONF_KEY_SAMPLE_EDITOR_Y] = conf.sampleEditorY;
- j[CONF_KEY_SAMPLE_EDITOR_W] = conf.sampleEditorW;
- j[CONF_KEY_SAMPLE_EDITOR_H] = conf.sampleEditorH;
- j[CONF_KEY_SAMPLE_EDITOR_GRID_VAL] = conf.sampleEditorGridVal;
- j[CONF_KEY_SAMPLE_EDITOR_GRID_ON] = conf.sampleEditorGridOn;
- j[CONF_KEY_PIANO_ROLL_Y] = conf.pianoRollY;
- j[CONF_KEY_PIANO_ROLL_H] = conf.pianoRollH;
- j[CONF_KEY_SAMPLE_ACTION_EDITOR_H] = conf.sampleActionEditorH;
- j[CONF_KEY_VELOCITY_EDITOR_H] = conf.velocityEditorH;
- j[CONF_KEY_ENVELOPE_EDITOR_H] = conf.envelopeEditorH;
- j[CONF_KEY_PLUGIN_LIST_X] = conf.pluginListX;
- j[CONF_KEY_PLUGIN_LIST_Y] = conf.pluginListY;
- j[CONF_KEY_MIDI_INPUT_X] = conf.midiInputX;
- j[CONF_KEY_MIDI_INPUT_Y] = conf.midiInputY;
- j[CONF_KEY_MIDI_INPUT_W] = conf.midiInputW;
- j[CONF_KEY_MIDI_INPUT_H] = conf.midiInputH;
- j[CONF_KEY_REC_TRIGGER_MODE] = static_cast<int>(conf.recTriggerMode);
- j[CONF_KEY_REC_TRIGGER_LEVEL] = conf.recTriggerLevel;
+ j[CONF_KEY_HEADER] = "GIADACFG";
+ j[CONF_KEY_LOG_MODE] = conf.logMode;
+ j[CONF_KEY_SOUND_SYSTEM] = conf.soundSystem;
+ j[CONF_KEY_SOUND_DEVICE_OUT] = conf.soundDeviceOut;
+ j[CONF_KEY_SOUND_DEVICE_IN] = conf.soundDeviceIn;
+ j[CONF_KEY_CHANNELS_OUT] = conf.channelsOut;
+ j[CONF_KEY_CHANNELS_IN_COUNT] = conf.channelsInCount;
+ j[CONF_KEY_CHANNELS_IN_START] = conf.channelsInStart;
+ j[CONF_KEY_SAMPLERATE] = conf.samplerate;
+ j[CONF_KEY_BUFFER_SIZE] = conf.buffersize;
+ j[CONF_KEY_LIMIT_OUTPUT] = conf.limitOutput;
+ j[CONF_KEY_RESAMPLE_QUALITY] = conf.rsmpQuality;
+ j[CONF_KEY_MIDI_SYSTEM] = conf.midiSystem;
+ j[CONF_KEY_MIDI_PORT_OUT] = conf.midiPortOut;
+ j[CONF_KEY_MIDI_PORT_IN] = conf.midiPortIn;
+ j[CONF_KEY_MIDIMAP_PATH] = conf.midiMapPath;
+ j[CONF_KEY_LAST_MIDIMAP] = conf.lastFileMap;
+ j[CONF_KEY_MIDI_SYNC] = conf.midiSync;
+ j[CONF_KEY_MIDI_TC_FPS] = conf.midiTCfps;
+ j[CONF_KEY_MIDI_IN] = conf.midiInEnabled;
+ j[CONF_KEY_MIDI_IN_FILTER] = conf.midiInFilter;
+ j[CONF_KEY_MIDI_IN_REWIND] = conf.midiInRewind;
+ j[CONF_KEY_MIDI_IN_START_STOP] = conf.midiInStartStop;
+ j[CONF_KEY_MIDI_IN_ACTION_REC] = conf.midiInActionRec;
+ j[CONF_KEY_MIDI_IN_INPUT_REC] = conf.midiInInputRec;
+ j[CONF_KEY_MIDI_IN_METRONOME] = conf.midiInMetronome;
+ j[CONF_KEY_MIDI_IN_VOLUME_IN] = conf.midiInVolumeIn;
+ j[CONF_KEY_MIDI_IN_VOLUME_OUT] = conf.midiInVolumeOut;
+ j[CONF_KEY_MIDI_IN_BEAT_DOUBLE] = conf.midiInBeatDouble;
+ j[CONF_KEY_MIDI_IN_BEAT_HALF] = conf.midiInBeatHalf;
+ j[CONF_KEY_CHANS_STOP_ON_SEQ_HALT] = conf.chansStopOnSeqHalt;
+ j[CONF_KEY_TREAT_RECS_AS_LOOPS] = conf.treatRecsAsLoops;
+ j[CONF_KEY_INPUT_MONITOR_DEFAULT_ON] = conf.inputMonitorDefaultOn;
+ j[CONF_KEY_OVERDUB_PROTECTION_DEFAULT_ON] = conf.overdubProtectionDefaultOn;
+ j[CONF_KEY_PLUGINS_PATH] = conf.pluginPath;
+ j[CONF_KEY_PATCHES_PATH] = conf.patchPath;
+ j[CONF_KEY_SAMPLES_PATH] = conf.samplePath;
+ j[CONF_KEY_MAIN_WINDOW_X] = conf.mainWindowX;
+ j[CONF_KEY_MAIN_WINDOW_Y] = conf.mainWindowY;
+ j[CONF_KEY_MAIN_WINDOW_W] = conf.mainWindowW;
+ j[CONF_KEY_MAIN_WINDOW_H] = conf.mainWindowH;
+ j[CONF_KEY_BROWSER_X] = conf.browserX;
+ j[CONF_KEY_BROWSER_Y] = conf.browserY;
+ j[CONF_KEY_BROWSER_W] = conf.browserW;
+ j[CONF_KEY_BROWSER_H] = conf.browserH;
+ j[CONF_KEY_BROWSER_POSITION] = conf.browserPosition;
+ j[CONF_KEY_BROWSER_LAST_PATH] = conf.browserLastPath;
+ j[CONF_KEY_BROWSER_LAST_VALUE] = conf.browserLastValue;
+ j[CONF_KEY_ACTION_EDITOR_X] = conf.actionEditorX;
+ j[CONF_KEY_ACTION_EDITOR_Y] = conf.actionEditorY;
+ j[CONF_KEY_ACTION_EDITOR_W] = conf.actionEditorW;
+ j[CONF_KEY_ACTION_EDITOR_H] = conf.actionEditorH;
+ j[CONF_KEY_ACTION_EDITOR_ZOOM] = conf.actionEditorZoom;
+ j[CONF_KEY_ACTION_EDITOR_GRID_VAL] = conf.actionEditorGridVal;
+ j[CONF_KEY_ACTION_EDITOR_GRID_ON] = conf.actionEditorGridOn;
+ j[CONF_KEY_SAMPLE_EDITOR_X] = conf.sampleEditorX;
+ j[CONF_KEY_SAMPLE_EDITOR_Y] = conf.sampleEditorY;
+ j[CONF_KEY_SAMPLE_EDITOR_W] = conf.sampleEditorW;
+ j[CONF_KEY_SAMPLE_EDITOR_H] = conf.sampleEditorH;
+ j[CONF_KEY_SAMPLE_EDITOR_GRID_VAL] = conf.sampleEditorGridVal;
+ j[CONF_KEY_SAMPLE_EDITOR_GRID_ON] = conf.sampleEditorGridOn;
+ j[CONF_KEY_PIANO_ROLL_Y] = conf.pianoRollY;
+ j[CONF_KEY_PIANO_ROLL_H] = conf.pianoRollH;
+ j[CONF_KEY_SAMPLE_ACTION_EDITOR_H] = conf.sampleActionEditorH;
+ j[CONF_KEY_VELOCITY_EDITOR_H] = conf.velocityEditorH;
+ j[CONF_KEY_ENVELOPE_EDITOR_H] = conf.envelopeEditorH;
+ j[CONF_KEY_PLUGIN_LIST_X] = conf.pluginListX;
+ j[CONF_KEY_PLUGIN_LIST_Y] = conf.pluginListY;
+ j[CONF_KEY_MIDI_INPUT_X] = conf.midiInputX;
+ j[CONF_KEY_MIDI_INPUT_Y] = conf.midiInputY;
+ j[CONF_KEY_MIDI_INPUT_W] = conf.midiInputW;
+ j[CONF_KEY_MIDI_INPUT_H] = conf.midiInputH;
+ j[CONF_KEY_REC_TRIGGER_MODE] = static_cast<int>(conf.recTriggerMode);
+ j[CONF_KEY_REC_TRIGGER_LEVEL] = conf.recTriggerLevel;
#ifdef WITH_VST
- j[CONF_KEY_PLUGIN_CHOOSER_X] = conf.pluginChooserX;
- j[CONF_KEY_PLUGIN_CHOOSER_Y] = conf.pluginChooserY;
- j[CONF_KEY_PLUGIN_CHOOSER_W] = conf.pluginChooserW;
- j[CONF_KEY_PLUGIN_CHOOSER_H] = conf.pluginChooserH;
- j[CONF_KEY_PLUGIN_SORT_METHOD] = conf.pluginSortMethod;
+ j[CONF_KEY_PLUGIN_CHOOSER_X] = conf.pluginChooserX;
+ j[CONF_KEY_PLUGIN_CHOOSER_Y] = conf.pluginChooserY;
+ j[CONF_KEY_PLUGIN_CHOOSER_W] = conf.pluginChooserW;
+ j[CONF_KEY_PLUGIN_CHOOSER_H] = conf.pluginChooserH;
+ j[CONF_KEY_PLUGIN_SORT_METHOD] = conf.pluginSortMethod;
#endif
std::ofstream ofs(confFilePath_);
ofs << j;
return true;
}
-}}}; // giada::m::conf::
\ No newline at end of file
+}}} // giada::m::conf::
\ No newline at end of file
{
struct Conf
{
- int logMode = LOG_MODE_MUTE;
- int soundSystem = G_DEFAULT_SOUNDSYS;
- int soundDeviceOut = G_DEFAULT_SOUNDDEV_OUT;
- int soundDeviceIn = G_DEFAULT_SOUNDDEV_IN;
- int channelsOut = 0;
- int channelsIn = 0;
- int samplerate = G_DEFAULT_SAMPLERATE;
- int buffersize = G_DEFAULT_BUFSIZE;
- bool limitOutput = false;
- int rsmpQuality = 0;
+ int logMode = LOG_MODE_MUTE;
+ int soundSystem = G_DEFAULT_SOUNDSYS;
+ int soundDeviceOut = G_DEFAULT_SOUNDDEV_OUT;
+ int soundDeviceIn = G_DEFAULT_SOUNDDEV_IN;
+ int channelsOut = 0;
+ int channelsInCount = 0;
+ int channelsInStart = 0;
+ int samplerate = G_DEFAULT_SAMPLERATE;
+ int buffersize = G_DEFAULT_BUFSIZE;
+ bool limitOutput = false;
+ int rsmpQuality = 0;
int midiSystem = 0;
int midiPortOut = G_DEFAULT_MIDI_PORT_OUT;
int midiSync = MIDI_SYNC_NONE;
float midiTCfps = 25.0f;
- bool recsStopOnChanHalt = false;
- bool chansStopOnSeqHalt = false;
- bool treatRecsAsLoops = false;
- bool inputMonitorDefaultOn = false;
+ bool chansStopOnSeqHalt = false;
+ bool treatRecsAsLoops = false;
+ bool inputMonitorDefaultOn = false;
+ bool overdubProtectionDefaultOn = false;
std::string pluginPath;
std::string patchPath;
void init();
bool read();
bool write();
-}}}; // giada::m::conf::
+}}} // giada::m::conf::
#endif
#define G_CONST_H
+#include <cstdint>
+
+
+/* -- debug ----------------------------------------------------------------- */
+#ifndef NDEBUG
+ #define G_DEBUG_MODE
+ #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
/* -- version --------------------------------------------------------------- */
constexpr auto G_APP_NAME = "Giada";
-constexpr auto G_VERSION_STR = "0.16.2";
+constexpr auto G_VERSION_STR = "0.17.1";
constexpr int G_VERSION_MAJOR = 0;
-constexpr int G_VERSION_MINOR = 16;
-constexpr int G_VERSION_PATCH = 2;
+constexpr int G_VERSION_MINOR = 17;
+constexpr int G_VERSION_PATCH = 1;
constexpr auto CONF_FILENAME = "giada.conf";
/* -- GUI ------------------------------------------------------------------- */
-constexpr float G_GUI_REFRESH_RATE = 0.05;
-constexpr float G_GUI_PLUGIN_RATE = 0.05; // refresh rate for plugin GUI
+constexpr float G_GUI_REFRESH_RATE = 1 / 30.0f; // 30 fps
+constexpr float G_GUI_PLUGIN_RATE = 1 / 30.0f; // 30 fps
constexpr int G_GUI_FONT_SIZE_BASE = 12;
constexpr int G_GUI_INNER_MARGIN = 4;
constexpr int G_GUI_OUTER_MARGIN = 8;
/* -- 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_VOLUME = 1.0f;
-constexpr int G_MAX_GRID_VAL = 64;
-constexpr int G_MIN_BUF_SIZE = 8;
-constexpr int G_MAX_BUF_SIZE = 4096;
-constexpr int G_MIN_GUI_WIDTH = 816;
-constexpr int G_MIN_GUI_HEIGHT = 510;
-constexpr int G_MAX_IO_CHANS = 2;
-constexpr int G_MAX_VELOCITY = 0x7F;
-constexpr int G_MAX_MIDI_CHANS = 16;
-constexpr int G_MAX_POLYPHONY = 32;
+constexpr float G_MIN_BPM = 20.0f;
+constexpr auto G_MIN_BPM_STR = "20.0";
+constexpr float G_MAX_BPM = 999.0f;
+constexpr auto G_MAX_BPM_STR = "999.0";
+constexpr int G_MAX_BEATS = 32;
+constexpr int G_MAX_BARS = 32;
+constexpr int G_MAX_QUANTIZE = 8;
+constexpr float G_MIN_DB_SCALE = 60.0f;
+constexpr int G_MIN_COLUMN_WIDTH = 140;
+constexpr float G_MAX_BOOST_DB = 20.0f;
+constexpr float G_MIN_PITCH = 0.1f;
+constexpr float G_MAX_PITCH = 4.0f;
+constexpr float G_MAX_PAN = 1.0f;
+constexpr float G_MAX_VOLUME = 1.0f;
+constexpr int G_MAX_GRID_VAL = 64;
+constexpr int G_MIN_BUF_SIZE = 8;
+constexpr int G_MAX_BUF_SIZE = 4096;
+constexpr int G_MIN_GUI_WIDTH = 816;
+constexpr int G_MIN_GUI_HEIGHT = 510;
+constexpr int G_MAX_IO_CHANS = 2;
+constexpr int G_MAX_VELOCITY = 0x7F;
+constexpr int G_MAX_MIDI_CHANS = 16;
+constexpr int G_MAX_POLYPHONY = 32;
+constexpr int G_MAX_QUEUE_EVENTS = 32;
+constexpr int G_MAX_QUANTIZER_SIZE = 8;
#define G_DEFAULT_SOUNDSYS G_SYS_API_CORE
#endif
-constexpr int G_DEFAULT_SOUNDDEV_OUT = 0; // FIXME - please override with rtAudio::getDefaultDevice (or similar)
-constexpr int G_DEFAULT_SOUNDDEV_IN = -1; // no recording by default: input disabled
-constexpr int G_DEFAULT_MIDI_SYSTEM = 0;
-constexpr int G_DEFAULT_MIDI_PORT_IN = -1;
-constexpr int G_DEFAULT_MIDI_PORT_OUT = -1;
-constexpr int G_DEFAULT_SAMPLERATE = 44100;
-constexpr int G_DEFAULT_BUFSIZE = 1024;
-constexpr int G_DEFAULT_BIT_DEPTH = 32; // float
-constexpr float G_DEFAULT_VOL = 1.0f;
-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_SOUNDDEV_OUT = 0; // FIXME - please override with rtAudio::getDefaultDevice (or similar)
+constexpr int G_DEFAULT_SOUNDDEV_IN = -1; // no recording by default: input disabled
+constexpr int G_DEFAULT_MIDI_SYSTEM = 0;
+constexpr int G_DEFAULT_MIDI_PORT_IN = -1;
+constexpr int G_DEFAULT_MIDI_PORT_OUT = -1;
+constexpr int G_DEFAULT_SAMPLERATE = 44100;
+constexpr int G_DEFAULT_BUFSIZE = 1024;
+constexpr int G_DEFAULT_BIT_DEPTH = 32; // float
+constexpr 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
Channel voices messages - controller (0xB0) is a special subset of this family:
it drives knobs, volume, faders and such. */
-#define MIDI_CONTROLLER 0xB0 << 24
-#define MIDI_ALL_NOTES_OFF (MIDI_CONTROLLER) | (0x7B << 16)
+constexpr uint32_t G_MIDI_CONTROLLER = static_cast<uint32_t>(0xB0 << 24);
+constexpr uint32_t G_MIDI_ALL_NOTES_OFF = (G_MIDI_CONTROLLER) | (0x7B << 16);
/* system common / real-time messages. Single bytes */
-#define MIDI_SYSEX 0xF0
-#define MIDI_MTC_QUARTER 0xF1
-#define MIDI_POSITION_PTR 0xF2
-#define MIDI_CLOCK 0xF8
-#define MIDI_START 0xFA
-#define MIDI_CONTINUE 0xFB
-#define MIDI_STOP 0xFC
-#define MIDI_EOX 0xF7 // end of sysex
-
-/* Channels */
-
-constexpr int G_MIDI_CHANS[G_MAX_MIDI_CHANS] = {
- 0x00 << 24, 0x01 << 24, 0x02 << 24, 0x03 << 24,
- 0x04 << 24, 0x05 << 24, 0x06 << 24, 0x07 << 24,
- 0x08 << 24, 0x09 << 24, 0x0A << 24, 0x0B << 24,
- 0x0C << 24, 0x0D << 24, 0x0E << 24, 0x0F << 24
-};
+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 */
-#define MIDI_SYNC_NONE 0x00
-#define MIDI_SYNC_CLOCK_M 0x01 // master
-#define MIDI_SYNC_CLOCK_S 0x02 // slave
-#define MIDI_SYNC_MTC_M 0x04 // master
-#define MIDI_SYNC_MTC_S 0x08 // slave
+constexpr int MIDI_SYNC_NONE = 0x00;
+constexpr int MIDI_SYNC_CLOCK_M = 0x01; // master
+constexpr int MIDI_SYNC_CLOCK_S = 0x02; // slave
+constexpr int MIDI_SYNC_MTC_M = 0x04; // master
+constexpr int MIDI_SYNC_MTC_S = 0x08; // slave
/* JSON patch keys */
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_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";
/* JSON config keys */
-constexpr auto CONF_KEY_HEADER = "header";
-constexpr auto CONF_KEY_LOG_MODE = "log_mode";
-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_IN = "channels_in";
-constexpr auto CONF_KEY_CHANNELS_OUT = "channels_out";
-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_RECS_STOP_ON_CHAN_HALT = "recs_stop_on_chan_halt";
-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_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_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_PIANO_ROLL_Y = "piano_roll_y";
-constexpr auto CONF_KEY_PIANO_ROLL_H = "piano_roll_h";
-constexpr auto CONF_KEY_SAMPLE_ACTION_EDITOR_H = "sample_action_editor_h";
-constexpr auto CONF_KEY_VELOCITY_EDITOR_H = "velocity_editor_h";
-constexpr auto CONF_KEY_ENVELOPE_EDITOR_H = "envelope_editor_h";
-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_HEADER = "header";
+constexpr auto CONF_KEY_LOG_MODE = "log_mode";
+constexpr auto CONF_KEY_SOUND_SYSTEM = "sound_system";
+constexpr auto CONF_KEY_SOUND_DEVICE_IN = "sound_device_in";
+constexpr auto CONF_KEY_SOUND_DEVICE_OUT = "sound_device_out";
+constexpr auto CONF_KEY_CHANNELS_OUT = "channels_out";
+constexpr auto CONF_KEY_CHANNELS_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_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_PIANO_ROLL_Y = "piano_roll_y";
+constexpr auto CONF_KEY_PIANO_ROLL_H = "piano_roll_h";
+constexpr auto CONF_KEY_SAMPLE_ACTION_EDITOR_H = "sample_action_editor_h";
+constexpr auto CONF_KEY_VELOCITY_EDITOR_H = "velocity_editor_h";
+constexpr auto CONF_KEY_ENVELOPE_EDITOR_H = "envelope_editor_h";
+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";
/* JSON midimaps keys */
" ",
" ",
" "};
+
+const char* armDisabled_xpm[] = {
+"18 18 7 1",
+" c None",
+". c #232523",
+"+ c #303230",
+"@ c #393B38",
+"# c #424441",
+"$ c #4B4D4A",
+"% c #4D4F4C",
+"..................",
+"..................",
+"..................",
+"..................",
+"......+#$$#+......",
+".....@%%%%%%@.....",
+"....+%%%%%%%%+....",
+"....#%%%%%%%%#....",
+"....$%%%%%%%%$....",
+"....$%%%%%%%%$....",
+"....#%%%%%%%%#....",
+"....+%%%%%%%%+....",
+".....@%%%%%%@.....",
+"......+#$$#+......",
+"..................",
+"..................",
+"..................",
+".................."};
\ No newline at end of file
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[];
#include "idManager.h"
-namespace giada {
-namespace m
+namespace giada::m
{
IdManager::IdManager() : m_id(0)
{
{
return id != 0 ? id : ++m_id;
}
-}} // giada::m::
+} // giada::m::
#include "core/types.h"
-namespace giada {
-namespace m
+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);
+
+ /* get
+ Generates a new unique id. If 'id' parameter is passed in is valid, it just
+ returns it with no unique id generation. Useful when loading things from the
+ model that already have their own id. */
+
ID get(ID id=0);
private:
ID m_id;
};
-}} // giada::m::
+} // giada::m::
#endif
#include "gui/dialogs/mainWindow.h"
#include "gui/dialogs/warnings.h"
#include "glue/main.h"
-#include "core/channels/channel.h"
+#include "core/model/storage.h"
#include "core/channels/channelManager.h"
#include "core/mixer.h"
#include "core/wave.h"
#include "core/const.h"
#include "core/clock.h"
#include "core/mixerHandler.h"
+#include "core/sequencer.h"
#include "core/patch.h"
#include "core/conf.h"
#include "core/waveManager.h"
-#include "core/pluginManager.h"
-#include "core/pluginHost.h"
+#include "core/plugins/pluginManager.h"
+#include "core/plugins/pluginHost.h"
#include "core/recorder.h"
#include "core/recorderHandler.h"
#include "core/recManager.h"
patch::init();
midimap::init();
midimap::setDefault();
+
+ model::load(conf::conf);
if (!u::log::init(conf::conf.logMode))
u::log::print("[init] log init failed! Using default stdout\n");
kernelAudio::openDevice();
clock::init(conf::conf.samplerate, conf::conf.midiTCfps);
mh::init();
+ sequencer::init();
recorder::init();
recorderHandler::init();
{
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().c_str());
- u::log::print("[init] RtMidi - %s\n", u::ver::getRtMidiVersion().c_str());
+ 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().c_str());
- u::log::print("[init] JSON for modern C++ - %d.%d.%d\n",
+ 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();
}
} // {anonymous}
waveManager::init();
clock::init(conf::conf.samplerate, conf::conf.midiTCfps);
mh::init();
+ sequencer::init();
recorder::init();
#ifdef WITH_VST
pluginManager::init(conf::conf.samplerate, kernelAudio::getRealBufSize());
{
shutdownGUI_();
+ model::store(conf::conf);
+
if (!conf::write())
u::log::print("[init] error while saving configuration file!\n");
else
#include "glue/main.h"
#include "core/model/model.h"
#include "conf.h"
+#include "const.h"
#include "mixer.h"
#include "const.h"
#include "kernelAudio.h"
-namespace giada {
-namespace m {
-namespace kernelAudio
+namespace giada::m::kernelAudio
{
namespace
{
unsigned realBufsize = 0; // Real buffer size from the soundcard
int api = 0;
-#if defined(__linux__) || defined(__FreeBSD__)
+#ifdef WITH_AUDIO_JACK
JackState jackState;
-jack_client_t* jackGetHandle()
+jack_client_t* jackGetHandle_()
{
return static_cast<jack_client_t*>(rtSystem->HACK__getJackClient());
}
#endif
-}; // {anonymous}
+} // {anonymous}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
+#ifdef WITH_AUDIO_JACK
+
+bool JackState::operator!=(const JackState& o) const
+{
+ return !(running == o.running && bpm == o.bpm && frame == o.frame);
+}
+
+#endif
+
+
+/* -------------------------------------------------------------------------- */
+
+
bool isReady()
{
model::KernelLock lock(model::kernel);
-
return model::kernel.get()->audioReady;
}
return 0;
}
- u::log::print("[KA] Opening devices %d (out), %d (in), f=%d...\n",
+ u::log::print("[KA] Opening device out=%d, in=%d, samplerate=%d\n",
conf::conf.soundDeviceOut, conf::conf.soundDeviceIn, conf::conf.samplerate);
numDevs = rtSystem->getDeviceCount();
else {
u::log::print("[KA] %d device(s) found\n", numDevs);
for (unsigned i=0; i<numDevs; i++)
- u::log::print(" %d) %s\n", i, getDeviceName(i).c_str());
+ u::log::print(" %d) %s\n", i, getDeviceName(i));
}
RtAudio::StreamParameters outParams;
outParams.nChannels = G_MAX_IO_CHANS;
outParams.firstChannel = conf::conf.channelsOut * G_MAX_IO_CHANS; // chan 0=0, 1=2, 2=4, ...
- /* inDevice can be disabled. */
+ /* Input device can be disabled. Unlike the output, here we are using all
+ channels and let the user choose which one to record from in the configuration
+ panel. */
if (conf::conf.soundDeviceIn != -1) {
inParams.deviceId = conf::conf.soundDeviceIn;
- inParams.nChannels = G_MAX_IO_CHANS;
- inParams.firstChannel = conf::conf.channelsIn * G_MAX_IO_CHANS; // chan 0=0, 1=2, 2=4, ...
+ inParams.nChannels = conf::conf.channelsInCount;
+ inParams.firstChannel = conf::conf.channelsInStart;
inputEnabled = true;
}
else
realBufsize = conf::conf.buffersize;
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
+#ifdef WITH_AUDIO_JACK
if (api == G_SYS_API_JACK) {
conf::conf.samplerate = getFreq(conf::conf.soundDeviceOut, 0);
- u::log::print("[KA] JACK in use, freq = %d\n", conf::conf.samplerate);
+ u::log::print("[KA] JACK in use, samplerate=%d\n", conf::conf.samplerate);
}
#endif
try {
rtSystem->openStream(
- &outParams, // output params
+ &outParams, // output params
conf::conf.soundDeviceIn != -1 ? &inParams : nullptr, // input params if inDevice is selected
- RTAUDIO_FLOAT32, // audio format
+ RTAUDIO_FLOAT32, // audio format
conf::conf.samplerate, // sample rate
- &realBufsize, // buffer size in byte
- &mixer::masterPlay, // audio callback
- nullptr, // user data (unused)
+ &realBufsize, // buffer size in byte
+ &mixer::masterPlay, // audio callback
+ nullptr, // user data (unused)
&options);
- std::unique_ptr<model::Kernel> k = model::kernel.clone();
- k->audioReady = true;
- model::kernel.swap(std::move(k));
-
+ model::onSwap(model::kernel, [](model::Kernel& k) {
+ k.audioReady = true;
+ });
return 1;
}
catch (RtAudioError &e) {
- u::log::print("[KA] rtSystem init error: %s\n", e.getMessage().c_str());
+ u::log::print("[KA] rtSystem init error: %s\n", e.getMessage());
closeDevice();
return 0;
}
return 1;
}
catch (RtAudioError &e) {
- u::log::print("[KA] Start stream error: %s\n", e.getMessage().c_str());
+ u::log::print("[KA] Start stream error: %s\n", e.getMessage());
return 0;
}
}
rtSystem->stopStream();
return 1;
}
- catch (RtAudioError &e) {
+ catch (RtAudioError& /*e*/) {
u::log::print("[KA] Stop stream error\n");
return 0;
}
try {
return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).name;
}
- catch (RtAudioError &e) {
+ catch (RtAudioError& /*e*/) {
u::log::print("[KA] invalid device ID = %d\n", dev);
return "";
}
try {
return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).inputChannels;
}
- catch (RtAudioError &e) {
+ catch (RtAudioError& /*e*/) {
u::log::print("[KA] Unable to get input channels\n");
return 0;
}
try {
return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).outputChannels;
}
- catch (RtAudioError &e) {
+ catch (RtAudioError& /*e*/) {
u::log::print("[KA] Unable to get output channels\n");
return 0;
}
try {
return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).probed;
}
- catch (RtAudioError &e) {
+ catch (RtAudioError& /*e*/) {
return 0;
}
}
try {
return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).duplexChannels;
}
- catch (RtAudioError &e) {
+ catch (RtAudioError& /*e*/) {
return 0;
}
}
try {
return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).isDefaultInput;
}
- catch (RtAudioError &e) {
+ catch (RtAudioError& /*e*/) {
return 0;
}
}
try {
return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).isDefaultOutput;
}
- catch (RtAudioError &e) {
+ catch (RtAudioError& /*e*/) {
return 0;
}
}
try {
return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).sampleRates.size();
}
- catch (RtAudioError &e) {
+ catch (RtAudioError& /*e*/) {
return 0;
}
}
try {
return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).sampleRates.at(i);
}
- catch (RtAudioError &e) {
+ catch (RtAudioError& /*e*/) {
return 0;
}
}
/* -------------------------------------------------------------------------- */
-#if defined(__linux__) || defined(__FreeBSD__)
+void logCompiledAPIs()
+{
+ std::vector<RtAudio::Api> APIs;
+ RtAudio::getCompiledApi(APIs);
+ u::log::print("[KA] Compiled RtAudio APIs: %d\n", APIs.size());
+
+ for (const RtAudio::Api& api : APIs) {
+ switch (api) {
+ case RtAudio::Api::LINUX_ALSA:
+ u::log::print(" ALSA\n"); break;
+ case RtAudio::Api::LINUX_PULSE:
+ u::log::print(" PulseAudio\n"); break;
+ case RtAudio::Api::UNIX_JACK:
+ u::log::print(" JACK\n"); break;
+ case RtAudio::Api::MACOSX_CORE:
+ u::log::print(" CoreAudio\n"); break;
+ case RtAudio::Api::WINDOWS_WASAPI:
+ u::log::print(" WASAPI\n"); break;
+ case RtAudio::Api::WINDOWS_ASIO:
+ u::log::print(" ASIO\n"); break;
+ case RtAudio::Api::WINDOWS_DS:
+ u::log::print(" DirectSound\n"); break;
+ case RtAudio::Api::RTAUDIO_DUMMY:
+ u::log::print(" Dummy\n"); break;
+ default:
+ u::log::print(" (unknown)\n"); break;
+ }
+ }
+}
-const JackState &jackTransportQuery()
+
+/* -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_AUDIO_JACK
+
+JackState jackTransportQuery()
{
if (api != G_SYS_API_JACK)
return jackState;
- jack_position_t position;
- jack_transport_state_t ts = jack_transport_query(jackGetHandle(), &position);
- jackState.running = ts != JackTransportStopped;
- jackState.bpm = position.beats_per_minute;
- jackState.frame = position.frame;
- return jackState;
+
+ jack_position_t position;
+ jack_transport_state_t ts = jack_transport_query(jackGetHandle_(), &position);
+
+ return {
+ ts != JackTransportStopped,
+ position.beats_per_minute,
+ position.frame
+ };
}
void jackStart()
{
if (api == G_SYS_API_JACK)
- jack_transport_start(jackGetHandle());
+ jack_transport_start(jackGetHandle_());
}
if (api != G_SYS_API_JACK)
return;
jack_position_t position;
- jack_transport_query(jackGetHandle(), &position);
+ jack_transport_query(jackGetHandle_(), &position);
position.frame = frame;
- jack_transport_reposition(jackGetHandle(), &position);
+ jack_transport_reposition(jackGetHandle_(), &position);
}
if (api != G_SYS_API_JACK)
return;
jack_position_t position;
- jack_transport_query(jackGetHandle(), &position);
+ jack_transport_query(jackGetHandle_(), &position);
position.valid = jack_position_bits_t::JackPositionBBT;
position.bar = 0; // no such info from Giada
position.beat = 0; // no such info from Giada
position.tick = 0; // no such info from Giada
position.beats_per_minute = bpm;
- jack_transport_reposition(jackGetHandle(), &position);
+ jack_transport_reposition(jackGetHandle_(), &position);
}
void jackStop()
{
if (api == G_SYS_API_JACK)
- jack_transport_stop(jackGetHandle());
+ jack_transport_stop(jackGetHandle_());
}
-#endif // defined(__linux__) || defined(__FreeBSD__)
-
-}}}; // giada::m::kernelAudio
+#endif // WITH_AUDIO_JACK
+} // giada::m::kernelAudio
#include <string>
-#if defined(__linux__) || defined(__FreeBSD__)
+#ifdef WITH_AUDIO_JACK
#include <jack/jack.h>
#include <jack/intclient.h>
#include <jack/transport.h>
#endif
-namespace giada {
-namespace m {
-namespace kernelAudio
+namespace giada::m::kernelAudio
{
-#if defined(__linux__) || defined(__FreeBSD__)
+#ifdef WITH_AUDIO_JACK
struct JackState
{
- bool running;
- double bpm;
- uint32_t frame;
+ bool running;
+ double bpm;
+ uint32_t frame;
+
+ bool operator!=(const JackState& o) const;
};
#endif
int getDefaultIn();
bool hasAPI(int API);
int getAPI();
+void logCompiledAPIs();
-#if defined(__linux__) || defined(__FreeBSD__)
+#ifdef WITH_AUDIO_JACK
void jackStart();
void jackStop();
void jackSetPosition(uint32_t frame);
void jackSetBpm(double bpm);
-const JackState &jackTransportQuery();
+JackState jackTransportQuery();
#endif
-}}}; // giada::m::kernelAudio::
+} // giada::m::kernelAudio::
+
#endif
* -------------------------------------------------------------------------- */
-#include "const.h"
-#ifdef G_OS_MAC
#include <RtMidi.h>
-#else
-#include <rtmidi/RtMidi.h>
-#endif
+#include "const.h"
#include "utils/log.h"
#include "midiDispatcher.h"
#include "midiMapConf.h"
unsigned numInPorts_ = 0;
-static void callback_(double t, std::vector<unsigned char>* msg, void* data)
+static void callback_(double /*t*/, std::vector<unsigned char>* msg, void* /*data*/)
{
if (msg->size() < 3) {
//u::log::print("[KM] MIDI received - unknown signal - size=%d, value=0x", (int) msg->size());
for (const midimap::Message& m : midimap::midimap.initCommands) {
if (m.value != 0x0 && m.channel != -1) {
u::log::print("[KM] MIDI send (init) - Channel %x - Event 0x%X\n", m.channel, m.value);
- send(m.value | G_MIDI_CHANS[m.channel]);
+ MidiEvent e(m.value);
+ e.setChannel(m.channel);
+ send(e.getRaw());
}
}
}
-
} // {anonymous}
status_ = true;
}
catch (RtMidiError &error) {
- u::log::print("[KM] MIDI out device error: %s\n", error.getMessage().c_str());
+ u::log::print("[KM] MIDI out device error: %s\n", error.getMessage());
status_ = false;
return 0;
}
numOutPorts_ = midiOut_->getPortCount();
u::log::print("[KM] %d output MIDI ports found\n", numOutPorts_);
for (unsigned i=0; i<numOutPorts_; i++)
- u::log::print(" %d) %s\n", i, getOutPortName(i).c_str());
+ u::log::print(" %d) %s\n", i, getOutPortName(i));
/* try to open a port, if enabled */
return 1;
}
catch (RtMidiError& error) {
- u::log::print("[KM] unable to open MIDI out port %d: %s\n", port, error.getMessage().c_str());
+ u::log::print("[KM] unable to open MIDI out port %d: %s\n", port, error.getMessage());
status_ = false;
return 0;
}
status_ = true;
}
catch (RtMidiError &error) {
- u::log::print("[KM] MIDI in device error: %s\n", error.getMessage().c_str());
+ u::log::print("[KM] MIDI in device error: %s\n", error.getMessage());
status_ = false;
return 0;
}
numInPorts_ = midiIn_->getPortCount();
u::log::print("[KM] %d input MIDI ports found\n", numInPorts_);
for (unsigned i=0; i<numInPorts_; i++)
- u::log::print(" %d) %s\n", i, getInPortName(i).c_str());
+ u::log::print(" %d) %s\n", i, getInPortName(i));
/* try to open a port, if enabled */
return 1;
}
catch (RtMidiError& error) {
- u::log::print("[KM] unable to open MIDI in port %d: %s\n", port, error.getMessage().c_str());
+ u::log::print("[KM] unable to open MIDI in port %d: %s\n", port, error.getMessage());
status_ = false;
return 0;
}
std::string getOutPortName(unsigned p)
{
try { return midiOut_->getPortName(p); }
- catch (RtMidiError &error) { return ""; }
+ catch (RtMidiError& /*error*/) { return ""; }
}
std::string getInPortName(unsigned p)
{
try { return midiIn_->getPortName(p); }
- catch (RtMidiError &error) { return ""; }
+ catch (RtMidiError& /*error*/) { return ""; }
}
msg.push_back(getB3(data));
midiOut_->sendMessage(&msg);
- u::log::print("[KM] send msg=0x%X (%X %X %X)\n", data, msg[0], msg[1], msg[2]);
+ u::log::print("[KM::send] send msg=0x%X (%X %X %X)\n", data, msg[0], msg[1], msg[2]);
}
msg.push_back(b3);
midiOut_->sendMessage(&msg);
- //u::log::print("[KM] send msg=(%X %X %X)\n", b1, b2, b3);
+ u::log::print("[KM::send] send msg=(%X %X %X)\n", b1, b2, b3);
}
/* -------------------------------------------------------------------------- */
-void sendMidiLightning(uint32_t learn, const midimap::Message& m)
+void sendMidiLightning(uint32_t learnt, const midimap::Message& m)
{
// Skip lightning message if not defined in midi map
if (!midimap::isDefined(m))
{
- u::log::print("[KM] message skipped (not defined in midimap)");
+ u::log::print("[KM::sendMidiLightning] message skipped (not defined in midimap)");
return;
}
- u::log::print("[KM] learn=%#X, chan=%d, msg=%#X, offset=%d\n", learn, m.channel,
- m.value, m.offset);
+ u::log::print("[KM::sendMidiLightning] learnt=0x%X, chan=%d, msg=0x%X, offset=%d\n",
+ learnt, m.channel, m.value, m.offset);
/* Isolate 'channel' from learnt message and offset it as requested by 'nn' in
the midimap configuration file. */
- uint32_t out = ((learn & 0x00FF0000) >> 16) << m.offset;
+ uint32_t out = ((learnt & 0x00FF0000) >> 16) << m.offset;
/* Merge the previously prepared channel into final message, and finally send
it. */
unsigned countOutPorts() { return numOutPorts_; }
bool getStatus() { return status_; }
+
/* -------------------------------------------------------------------------- */
{
return (b1 << 24) | (b2 << 16) | (b3 << 8) | (0x00);
}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-uint32_t setChannel(uint32_t iValue, int channel)
-{
- uint32_t chanMask = 0xF << 24;
- return (iValue & (~chanMask)) | (channel << 24);
-}
-
-}}}; // giada::m::kernelMidi::
+}}} // giada::m::kernelMidi::
uint32_t getIValue(int b1, int b2, int b3);
-/* setChannel
-Changes MIDI channel number inside iValue. Returns new message with updated
-channel. */
-
-uint32_t setChannel(uint32_t iValue, int channel);
-
/* send
Sends a MIDI message 's' as uint32_t or as separate bytes. */
/* sendMidiLightning
Sends a MIDI lightning message defined by 'msg'. */
-void sendMidiLightning(uint32_t learn, const midimap::Message& msg);
+void sendMidiLightning(uint32_t learnt, const midimap::Message& msg);
/* setApi
Sets the Api in use for both in & out messages. */
bool hasAPI(int API);
-}}}; // giada::m::kernelMidi::
+}}} // giada::m::kernelMidi::
#endif
#include <cassert>
#include <vector>
#include "glue/plugin.h"
-#include "glue/io.h"
-#include "glue/channel.h"
-#include "glue/main.h"
+#include "glue/events.h"
#include "utils/log.h"
#include "utils/math.h"
#include "core/model/model.h"
-#include "core/channels/channel.h"
-#include "core/channels/sampleChannel.h"
-#include "core/channels/midiChannel.h"
#include "core/conf.h"
#include "core/mixer.h"
#include "core/mixerHandler.h"
-#include "core/pluginHost.h"
-#include "core/plugin.h"
+#include "core/plugins/pluginHost.h"
+#include "core/plugins/plugin.h"
#include "core/recManager.h"
#include "core/types.h"
#include "core/midiDispatcher.h"
bool isChannelMidiInAllowed_(ID channelId, int c)
{
model::ChannelsLock l(model::channels);
- return model::get(model::channels, channelId).isMidiInAllowed(c);
+ return model::get(model::channels, channelId).midiLearner.state->isAllowed(c);
}
/* Plugins' parameters layout reflects the structure of the matrix
Channel::midiInPlugins. It is safe to assume then that Plugin 'p' and
- k indexes match both the structure of Channel::midiInPlugins and the vector
- of plugins. */
+ parameter indexes match both the structure of Channel::midiInPlugins and the
+ vector of plugins. */
m::model::PluginsLock l(m::model::plugins);
-
for (ID id : ids) {
- m::Plugin& p = m::model::get(m::model::plugins, id);
- for (unsigned k = 0; k < p.midiInParams.size(); k++) {
- if (pure != p.midiInParams.at(k))
+ const m::Plugin& p = m::model::get(m::model::plugins, id);
+ for (const MidiLearnParam& param : p.midiInParams) {
+ if (pure != param.getValue())
continue;
- c::plugin::setParameter(id, k, vf, /*gui=*/false);
- u::log::print(" >>> [plugin %d parameter %d] (pure=0x%X, value=%d, float=%f)\n",
- p.id, k, pure, midiEvent.getVelocity(), vf);
+ c::events::setPluginParameter(id, param.getIndex(), vf, /*gui=*/false);
+ u::log::print(" >>> [pluginId=%d paramIndex=%d] (pure=0x%X, value=%d, float=%f)\n",
+ p.id, param.getIndex(), pure, midiEvent.getVelocity(), vf);
}
}
}
{
uint32_t pure = midiEvent.getRawNoVelocity();
- /* TODO - this is definitely not the best approach but it's necessary as
- you can't call actions on m::model::channels while locking on a upper
- level. Let's wait for a better async mechanism... */
-
- std::vector<std::function<void()>> actions;
+ model::ChannelsLock lock(model::channels);
- model::channels.lock();
- for (Channel* ch : model::channels) {
+ for (const Channel* c : model::channels) {
/* Do nothing on this channel if MIDI in is disabled or filtered out for
the current MIDI channel. */
- if (!ch->midiIn || !ch->isMidiInAllowed(midiEvent.getChannel()))
+ if (!c->midiLearner.state->isAllowed(midiEvent.getChannel()))
continue;
- if (pure == ch->midiInKeyPress) {
- actions.push_back([=] {
- u::log::print(" >>> keyPress, ch=%d (pure=0x%X)\n", ch->id, pure);
- c::io::keyPress(ch->id, false, false, midiEvent.getVelocity());
- });
+ if (pure == c->midiLearner.state->keyPress.getValue()) {
+ u::log::print(" >>> keyPress, ch=%d (pure=0x%X)\n", c->id, pure);
+ c::events::pressChannel(c->id, midiEvent.getVelocity(), Thread::MIDI);
}
- else if (pure == ch->midiInKeyRel) {
- actions.push_back([=] {
- u::log::print(" >>> keyRel ch=%d (pure=0x%X)\n", ch->id, pure);
- c::io::keyRelease(ch->id, false, false);
- });
+ else if (pure == c->midiLearner.state->keyRelease.getValue()) {
+ u::log::print(" >>> keyRel ch=%d (pure=0x%X)\n", c->id, pure);
+ c::events::releaseChannel(c->id, Thread::MIDI);
}
- else if (pure == ch->midiInMute) {
- actions.push_back([=] {
- u::log::print(" >>> mute ch=%d (pure=0x%X)\n", ch->id, pure);
- c::channel::toggleMute(ch->id);
- });
+ else if (pure == c->midiLearner.state->mute.getValue()) {
+ u::log::print(" >>> mute ch=%d (pure=0x%X)\n", c->id, pure);
+ c::events::toggleMuteChannel(c->id, Thread::MIDI);
}
- else if (pure == ch->midiInKill) {
- actions.push_back([=] {
- u::log::print(" >>> kill ch=%d (pure=0x%X)\n", ch->id, pure);
- c::channel::kill(ch->id, /*record=*/false);
- });
+ else if (pure == c->midiLearner.state->kill.getValue()) {
+ u::log::print(" >>> kill ch=%d (pure=0x%X)\n", c->id, pure);
+ c::events::killChannel(c->id, Thread::MIDI);
}
- else if (pure == ch->midiInArm) {
- actions.push_back([=] {
- u::log::print(" >>> arm ch=%d (pure=0x%X)\n", ch->id, pure);
- c::channel::toggleArm(ch->id);
- });
+ else if (pure == c->midiLearner.state->arm.getValue()) {
+ u::log::print(" >>> arm ch=%d (pure=0x%X)\n", c->id, pure);
+ c::events::toggleArmChannel(c->id, Thread::MIDI);
}
- else if (pure == ch->midiInSolo) {
- actions.push_back([=] {
- u::log::print(" >>> solo ch=%d (pure=0x%X)\n", ch->id, pure);
- c::channel::toggleSolo(ch->id);
- });
+ else if (pure == c->midiLearner.state->solo.getValue()) {
+ u::log::print(" >>> solo ch=%d (pure=0x%X)\n", c->id, pure);
+ c::events::toggleSoloChannel(c->id, Thread::MIDI);
}
- else if (pure == ch->midiInVolume) {
- actions.push_back([=] {
- 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",
- ch->id, pure, midiEvent.getVelocity(), vf);
- c::channel::setVolume(ch->id, vf, /*gui=*/false);
- });
+ else if (pure == c->midiLearner.state->volume.getValue()) {
+ float vf = u::math::map(midiEvent.getVelocity(), G_MAX_VELOCITY, G_MAX_VOLUME);
+ u::log::print(" >>> volume ch=%d (pure=0x%X, value=%d, float=%f)\n",
+ c->id, pure, midiEvent.getVelocity(), vf);
+ c::events::setChannelVolume(c->id, vf, Thread::MIDI);
}
- else {
- const SampleChannel* sch = static_cast<const SampleChannel*>(ch);
- if (pure == sch->midiInPitch) {
- actions.push_back([=] {
- 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",
- sch->id, pure, midiEvent.getVelocity(), vf);
- c::channel::setPitch(sch->id, vf);
- });
- }
- else
- if (pure == sch->midiInReadActions) {
- actions.push_back([=] {
- u::log::print(" >>> toggle read actions ch=%d (pure=0x%X)\n", sch->id, pure);
- c::channel::toggleReadingActions(sch->id);
- });
- }
+ else if (pure == c->midiLearner.state->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.state->readActions.getValue()) {
+ u::log::print(" >>> toggle read actions ch=%d (pure=0x%X)\n", c->id, pure);
+ c::events::toggleReadActionsChannel(c->id, Thread::MIDI);
}
-#ifdef WITH_VST
+#ifdef WITH_VST
/* Process learned plugins parameters. */
- processPlugins_(ch->pluginIds, midiEvent);
-
+ processPlugins_(c->pluginIds, midiEvent);
#endif
- /* Redirect full midi message (pure + velocity) to plugins. */
- ch->receiveMidi(midiEvent.getRaw());
- }
- model::channels.unlock();
+ /* Redirect raw MIDI message (pure + velocity) to plug-ins in armed
+ channels. */
- /* Apply all the collected actions. */
- for (auto& action : actions)
- action();
+ if (c->state->armed.load() == true)
+ c::events::sendMidiToChannel(c->id, midiEvent, Thread::MIDI);
+ }
}
const model::MidiIn* midiIn = model::midiIn.get();
if (pure == midiIn->rewind) {
- mh::rewindSequencer();
+ c::events::rewindSequencer(Thread::MIDI);
u::log::print(" >>> rewind (master) (pure=0x%X)\n", pure);
}
else if (pure == midiIn->startStop) {
- mh::toggleSequencer();
+ c::events::toggleSequencer(Thread::MIDI);
u::log::print(" >>> startStop (master) (pure=0x%X)\n", pure);
}
else if (pure == midiIn->actionRec) {
- recManager::toggleActionRec(conf::conf.recTriggerMode);
+ c::events::toggleActionRecording();
u::log::print(" >>> actionRec (master) (pure=0x%X)\n", pure);
}
else if (pure == midiIn->inputRec) {
- c::main::toggleInputRec();
+ c::events::toggleInputRecording();
u::log::print(" >>> inputRec (master) (pure=0x%X)\n", pure);
}
else if (pure == midiIn->metronome) {
- m::mixer::toggleMetronome();
+ 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::main::setInVol(vf, /*gui=*/false);
+ 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::main::setOutVol(vf, /*gui=*/false);
+ 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::main::beatsMultiply();
+ c::events::multiplyBeats();
u::log::print(" >>> sequencer x2 (master) (pure=0x%X)\n", pure);
}
else if (pure == midiIn->beatHalf) {
- c::main::beatsDivide();
+ c::events::divideBeats();
u::log::print(" >>> sequencer /2 (master) (pure=0x%X)\n", pure);
}
}
uint32_t raw = e.getRawNoVelocity();
- model::onSwap(model::channels, channelId, [&](Channel& c)
+ model::onGet(model::channels, channelId, [param, raw](Channel& c)
{
switch (param) {
- case G_MIDI_IN_KEYPRESS: c.midiInKeyPress = raw; break;
- case G_MIDI_IN_KEYREL: c.midiInKeyRel = raw; break;
- case G_MIDI_IN_KILL: c.midiInKill = raw; break;
- case G_MIDI_IN_ARM: c.midiInArm = raw; break;
- case G_MIDI_IN_MUTE: c.midiInVolume = raw; break;
- case G_MIDI_IN_SOLO: c.midiInMute = raw; break;
- case G_MIDI_IN_VOLUME: c.midiInVolume = raw; break;
- case G_MIDI_IN_PITCH: static_cast<SampleChannel&>(c).midiInPitch = raw; break;
- case G_MIDI_IN_READ_ACTIONS: static_cast<SampleChannel&>(c).midiInReadActions = raw; break;
+ case G_MIDI_IN_KEYPRESS: c.midiLearner.state->keyPress.setValue(raw); break;
+ case G_MIDI_IN_KEYREL: c.midiLearner.state->keyRelease.setValue(raw); break;
+ case G_MIDI_IN_KILL: c.midiLearner.state->kill.setValue(raw); break;
+ case G_MIDI_IN_ARM: c.midiLearner.state->arm.setValue(raw); break;
+ case G_MIDI_IN_MUTE: c.midiLearner.state->mute.setValue(raw); break;
+ case G_MIDI_IN_SOLO: c.midiLearner.state->solo.setValue(raw); break;
+ case G_MIDI_IN_VOLUME: c.midiLearner.state->volume.setValue(raw); break;
+ case G_MIDI_IN_PITCH: c.midiLearner.state->pitch.setValue(raw); break;
+ case G_MIDI_IN_READ_ACTIONS: c.midiLearner.state->readActions.setValue(raw); break;
+ case G_MIDI_OUT_L_PLAYING: c.midiLighter.state->playing.setValue(raw); break;
+ case G_MIDI_OUT_L_MUTE: c.midiLighter.state->mute.setValue(raw); break;
+ case G_MIDI_OUT_L_SOLO: c.midiLighter.state->solo.setValue(raw); break;
}
});
uint32_t raw = e.getRawNoVelocity();
- model::onSwap(model::midiIn, [&](model::MidiIn& m)
+ model::onSwap(model::midiIn, [param, raw](model::MidiIn& m)
{
switch (param) {
case G_MIDI_IN_REWIND: m.rewind = raw; break;
case G_MIDI_IN_START_STOP: m.startStop = raw; break;
case G_MIDI_IN_ACTION_REC: m.actionRec = raw; break;
case G_MIDI_IN_INPUT_REC: m.inputRec = raw; break;
- case G_MIDI_IN_METRONOME: m.volumeIn = raw; break;
- case G_MIDI_IN_VOLUME_IN: m.volumeOut = raw; break;
- case G_MIDI_IN_VOLUME_OUT: m.beatDouble = raw; break;
- case G_MIDI_IN_BEAT_DOUBLE: m.beatHalf = raw; break;
- case G_MIDI_IN_BEAT_HALF: m.metronome = raw; break;
+ case G_MIDI_IN_METRONOME: m.metronome = raw; break;
+ case G_MIDI_IN_VOLUME_IN: m.volumeIn = raw; break;
+ case G_MIDI_IN_VOLUME_OUT: m.volumeOut = raw; break;
+ case G_MIDI_IN_BEAT_DOUBLE: m.beatDouble = raw; break;
+ case G_MIDI_IN_BEAT_HALF: m.beatHalf = raw; break;
}
});
#ifdef WITH_VST
-void learnPlugin_(MidiEvent e, int paramIndex, ID pluginId, std::function<void()> doneCb)
+void learnPlugin_(MidiEvent e, std::size_t paramIndex, ID pluginId, std::function<void()> doneCb)
{
- model::onSwap(model::plugins, pluginId, [&](Plugin& p)
+ model::onGet(model::plugins, pluginId, [&](Plugin& p)
{
- p.midiInParams[paramIndex] = e.getRawNoVelocity();
+ assert(paramIndex < p.midiInParams.size());
+ p.midiInParams[paramIndex].setValue(e.getRawNoVelocity());
});
stopLearn();
#ifdef WITH_VST
-void startPluginLearn (int paramIndex, ID pluginId, std::function<void()> f)
+void startPluginLearn (std::size_t paramIndex, ID pluginId, std::function<void()> f)
{
learnCb_ = [=](m::MidiEvent e) { learnPlugin_(e, paramIndex, pluginId, f); };
}
#ifdef WITH_VST
-void clearPluginLearn (int paramIndex, ID pluginId, std::function<void()> f)
+void clearPluginLearn (std::size_t paramIndex, ID pluginId, std::function<void()> f)
{
learnPlugin_(MidiEvent(), paramIndex, pluginId, f); // Empty event (0x0)
}
signalCb_ = f;
}
-}}}; // giada::m::midiDispatcher::
+}}} // giada::m::midiDispatcher::
void clearMasterLearn (int param, std::function<void()> f);
void clearChannelLearn(int param, ID channelId, std::function<void()> f);
#ifdef WITH_VST
-void startPluginLearn (int paramIndex, ID pluginId, std::function<void()> f);
-void clearPluginLearn (int paramIndex, ID pluginId, std::function<void()> f);
+void startPluginLearn (std::size_t paramIndex, ID pluginId, std::function<void()> f);
+void clearPluginLearn (std::size_t paramIndex, ID pluginId, std::function<void()> f);
#endif
void dispatch(int byte1, int byte2, int byte3);
void setSignalCallback(std::function<void()> f);
-}}}; // giada::m::midiDispatcher::
+}}} // giada::m::midiDispatcher::
#endif
#include <cassert>
#include "const.h"
+#include "utils/math.h"
#include "midiEvent.h"
namespace giada {
namespace m
{
-MidiEvent::MidiEvent()
- : m_status (0),
- m_channel (0),
- m_note (0),
- m_velocity(0),
- m_delta (0)
+namespace
{
-}
+constexpr int FLOAT_FACTOR = 10000;
+} // {anonymous}
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
MidiEvent::MidiEvent(uint32_t raw)
- : m_status ((raw & 0xF0000000) >> 24),
- m_channel ((raw & 0x0F000000) >> 24),
- m_note ((raw & 0x00FF0000) >> 16),
- m_velocity((raw & 0x0000FF00) >> 8),
- m_delta (0) // not used
+: m_status ((raw & 0xF0000000) >> 24)
+, m_channel ((raw & 0x0F000000) >> 24)
+, m_note ((raw & 0x00FF0000) >> 16)
+, m_velocity((raw & 0x0000FF00) >> 8)
+, m_delta (0) // not used
{
}
/* -------------------------------------------------------------------------- */
+/* static_cast to avoid ambiguity with MidiEvent(float). */
MidiEvent::MidiEvent(int byte1, int byte2, int byte3)
- : MidiEvent((byte1 << 24) | (byte2 << 16) | (byte3 << 8) | (0x00))
+: MidiEvent(static_cast<uint32_t>((byte1 << 24) | (byte2 << 16) | (byte3 << 8) | (0x00)))
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MidiEvent::MidiEvent(float v)
+: MidiEvent(ENVELOPE, 0, 0)
{
+ m_velocity = static_cast<int>(v * FLOAT_FACTOR);
}
}
+float MidiEvent::getVelocityFloat() const
+{
+ return m_velocity / static_cast<float>(FLOAT_FACTOR);
+}
+
+
bool MidiEvent::isNoteOnOff() const
{
return m_status == NOTE_ON || m_status == NOTE_OFF;
static const int NOTE_KILL = 0x70;
static const int ENVELOPE = 0xB0;
- MidiEvent();
+ /* MidiEvent (1)
+ Creates and empty and invalid MIDI event. */
+
+ MidiEvent() = default;
+
MidiEvent(uint32_t raw);
MidiEvent(int byte1, int byte2, int byte3);
+ /* MidiEvent (4)
+ A constructor that takes a float parameter. Useful to build ENVELOPE events
+ for automations, volume and pitch. */
+
+ MidiEvent(float v);
+
int getStatus() const;
int getChannel() const;
int getNote() const;
- int getVelocity() const;
+ int getVelocity() const;
+ float getVelocityFloat() const;
bool isNoteOnOff() const;
int getDelta() const;
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "midiLearnParam.h"
+
+
+namespace giada::m
+{
+MidiLearnParam::MidiLearnParam()
+: m_param(0x0)
+, m_index(0)
+{
+}
+
+
+MidiLearnParam::MidiLearnParam(uint32_t v, std::size_t index)
+: m_param(v)
+, m_index(index)
+{
+}
+
+
+MidiLearnParam::MidiLearnParam(const MidiLearnParam& o)
+: m_param(o.m_param.load(std::memory_order_relaxed))
+, m_index(o.m_index)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+uint32_t MidiLearnParam::getValue() const
+{
+ return m_param.load(std::memory_order_relaxed);
+}
+
+
+void MidiLearnParam::setValue(uint32_t v)
+{
+ m_param.store(v, std::memory_order_relaxed);
+}
+
+
+std::size_t MidiLearnParam::getIndex() const
+{
+ return m_index;
+}
+} // giada::m::
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_MIDI_LEARN_PARAM_H
+#define G_MIDI_LEARN_PARAM_H
+
+
+#include <atomic>
+
+
+namespace giada::m
+{
+class MidiLearnParam
+{
+public:
+
+ MidiLearnParam();
+ MidiLearnParam(uint32_t v, std::size_t index=0);
+ MidiLearnParam(const MidiLearnParam& o);
+
+ uint32_t getValue() const;
+ std::size_t getIndex() const;
+ void setValue(uint32_t v);
+
+private:
+
+ std::atomic<uint32_t> m_param;
+ std::size_t m_index;
+};
+} // giada::m::
+
+
+#endif
#include <vector>
#include <string>
#include <cstring>
-#include <dirent.h>
+#include <filesystem>
#include "deps/json/single_include/nlohmann/json.hpp"
#include "utils/string.h"
#include "utils/log.h"
std::string input = message.valueStr;
- size_t f = input.find("0x"); // check if "0x" is there
+ std::size_t f = input.find("0x"); // check if "0x" is there
if (f != std::string::npos)
input = message.valueStr.replace(f, 2, "");
message.value = strtoul(output.c_str(), nullptr, 16);
u::log::print("[parse] parsed chan=%d valueStr=%s value=%#x, offset=%d\n",
- message.channel, message.valueStr.c_str(), message.value, message.offset);
+ message.channel, message.valueStr, message.value, message.offset);
}
-
-}; // {anonymous}
+} // {anonymous}
/* -------------------------------------------------------------------------- */
/* scan dir of midi maps and load the filenames into <>maps. */
u::log::print("[midiMapConf::init] scanning midimaps directory '%s'...\n",
- midimapsPath.c_str());
-
- DIR* dp;
- dirent* ep;
- dp = opendir(midimapsPath.c_str());
+ midimapsPath);
- if (dp == nullptr) {
+ if (!std::filesystem::exists(midimapsPath)) {
u::log::print("[midiMapConf::init] unable to scan midimaps directory!\n");
return;
}
- while ((ep = readdir(dp))) {
- if (!strcmp(ep->d_name, ".") || !strcmp(ep->d_name, ".."))
- continue;
-
+ for (const auto& d : std::filesystem::directory_iterator(midimapsPath)) {
// TODO - check if is a valid midimap file (verify headers)
-
- u::log::print("[midiMapConf::init] found midimap '%s'\n", ep->d_name);
-
- maps.push_back(ep->d_name);
+ if (!d.is_regular_file())
+ continue;
+ u::log::print("[midiMapConf::init] found midimap '%s'\n", d.path().filename().string());
+ maps.push_back(d.path().filename().string());
}
u::log::print("[midiMapConf::init] total midimaps found: %d\n", maps.size());
- closedir(dp);
}
return MIDIMAP_NOT_SPECIFIED;
}
- u::log::print("[midiMapConf::read] reading midimap file '%s'\n", file.c_str());
+ u::log::print("[midiMapConf::read] reading midimap file '%s'\n", file);
std::ifstream ifs(midimapsPath + file);
if (!ifs.good())
return MIDIMAP_READ_OK;
}
-}}}; // giada::m::midimap::
+}}} // giada::m::midimap::
Reads a midi map from file 'file'. */
int read(const std::string& file);
-}}}; // giada::m::midimap::
+}}} // giada::m::midimap::
#endif
#include "utils/log.h"
#include "utils/math.h"
#include "core/model/model.h"
-#include "core/channels/channel.h"
-#include "core/channels/sampleChannel.h"
-#include "core/channels/midiChannel.h"
#include "core/wave.h"
#include "core/kernelAudio.h"
#include "core/recorder.h"
#include "core/recManager.h"
-#include "core/pluginHost.h"
+#include "core/plugins/pluginHost.h"
#include "core/conf.h"
#include "core/mixerHandler.h"
#include "core/clock.h"
#include "core/const.h"
#include "core/audioBuffer.h"
#include "core/action.h"
+#include "core/sequencer.h"
#include "core/mixer.h"
{
namespace
{
-struct Metronome
-{
- static constexpr Frame CLICK_SIZE = 38;
-
- float beat[CLICK_SIZE] = {
- 0.059033, 0.117240, 0.173807, 0.227943, 0.278890, 0.325936,
- 0.368423, 0.405755, 0.437413, 0.462951, 0.482013, 0.494333,
- 0.499738, 0.498153, 0.489598, 0.474195, 0.452159, 0.423798,
- 0.389509, 0.349771, 0.289883, 0.230617, 0.173194, 0.118739,
- 0.068260, 0.022631, -0.017423, -0.051339, -0.078721, -0.099345,
- -0.113163, -0.120295, -0.121028, -0.115804, -0.105209, -0.089954,
- -0.070862, -0.048844
- };
-
- float bar[CLICK_SIZE] = {
- 0.175860, 0.341914, 0.488904, 0.608633, 0.694426, 0.741500,
- 0.747229, 0.711293, 0.635697, 0.524656, 0.384362, 0.222636,
- 0.048496, -0.128348, -0.298035, -0.451105, -0.579021, -0.674653,
- -0.732667, -0.749830, -0.688924, -0.594091, -0.474481, -0.340160,
- -0.201360, -0.067752, 0.052194, 0.151746, 0.226280, 0.273493,
- 0.293425, 0.288307, 0.262252, 0.220811, 0.170435, 0.117887,
- 0.069639, 0.031320
- };
-
- Frame tracker = 0;
- bool running = false;
- bool playBar = false;
- bool playBeat = false;
-
- void render(AudioBuffer& outBuf, bool& process, float* data, Frame f)
- {
- process = true;
- for (int i=0; i<outBuf.countChannels(); i++)
- outBuf[f][i] += data[tracker];
- if (++tracker > Metronome::CLICK_SIZE) {
- process = false;
- tracker = 0;
- }
- }
-} metronome_;
-
-/* vChanInput_
-Virtual channel for input recording. */
+/* recBuffer_
+Working buffer for audio recording. */
-AudioBuffer vChanInput_;
+AudioBuffer recBuffer_;
-/* vChanInToOut_
-Virtual channel in->out bridge (hear what you're playin). */
+/* inBuffer_
+Working buffer for input channel. Used for the in->out bridge. */
-AudioBuffer vChanInToOut_;
+AudioBuffer inBuffer_;
/* inputTracker_
Frame position while recording. */
std::atomic<bool> processing_(false);
std::atomic<bool> active_(false);
+/* eventBuffer_
+Buffer of events sent to channels for event parsing. This is filled with Events
+coming from the two event queues.*/
+
+EventBuffer eventBuffer_;
+
/* -------------------------------------------------------------------------- */
-void computePeak_(const AudioBuffer& buf, std::atomic<float>& peak)
+bool isChannelAudible_(const Channel& c)
{
- for (int i=0; i<buf.countFrames(); i++)
- for (int j=0; j<buf.countChannels(); j++)
- if (buf[i][j] > peak)
- peak = buf[i][j];
+ if (c.getType() == ChannelType::MASTER || c.getType() == ChannelType::PREVIEW)
+ return true;
+ if (c.state->mute.load() == true)
+ return false;
+ model::MixerLock ml(model::mixer);
+ bool hasSolos = model::mixer.get()->hasSolos;
+ return !hasSolos || (hasSolos && c.state->solo.load() == true);
}
{
if (!recManager::isRecordingInput() || !kernelAudio::isInputEnabled())
return;
+
+ float inVol = mh::getInVol();
+ int framesInLoop = clock::getFramesInLoop();
- for (int i=0; i<inBuf.countFrames(); i++, inputTracker_++)
- for (int j=0; j<inBuf.countChannels(); j++) {
- if (inputTracker_ >= clock::getFramesInLoop())
- inputTracker_ = 0;
- vChanInput_[inputTracker_][j] += inBuf[i][j] * mh::getInVol(); // adding: overdub!
- }
+ for (int i = 0; i < inBuf.countFrames(); i++, inputTracker_++)
+ for (int j = 0; j < inBuf.countChannels(); j++)
+ recBuffer_[inputTracker_ % framesInLoop][j] += inBuf[i][j] * inVol; // adding: overdub!
}
/* -------------------------------------------------------------------------- */
/* processLineIn
-Computes line in peaks, plus handles "hear what you're playin'" thing. */
+Computes line in peaks and prepares the internal working buffer for input
+recording. */
void processLineIn_(const AudioBuffer& inBuf)
{
if (!kernelAudio::isInputEnabled())
return;
- computePeak_(inBuf, peakIn);
+ peakIn.store(inBuf.getPeak());
if (signalCb_ != nullptr && u::math::linearToDB(peakIn) > conf::conf.recTriggerLevel) {
+G_DEBUG("Signal > threshold!");
signalCb_();
signalCb_ = nullptr;
}
- /* "hear what you're playing" - process, copy and paste the input buffer onto
- the output buffer. */
+ /* Prepare the working buffer for input stream, which will be processed
+ later on by the Master Input Channel with plug-ins. */
+
+ assert(inBuf.countChannels() <= inBuffer_.countChannels());
model::MixerLock lock(model::mixer);
-
- if (model::mixer.get()->inToOut)
- for (int i=0; i<vChanInToOut_.countFrames(); i++)
- for (int j=0; j<vChanInToOut_.countChannels(); j++)
- vChanInToOut_[i][j] = inBuf[i][j] * mh::getInVol();
+ inBuffer_.copyData(inBuf, mh::getInVol());
}
/* -------------------------------------------------------------------------- */
-/* doQuantize
-Computes quantization on 'rewind' button. */
-void doQuantize_(unsigned frame)
+void fillEventBuffer_()
{
- /* Nothing to do if quantizer disabled or a quanto has not passed yet. */
+ eventBuffer_.clear();
- if (clock::getQuantize() == 0 || !clock::quantoHasPassed())
- return;
+ Event e;
+ while (UIevents.pop(e)) eventBuffer_.push_back(e);
+ while (MidiEvents.pop(e)) eventBuffer_.push_back(e);
- if (rewindWait) {
- rewindWait = false;
- clock::rewind();
- mh::rewindChannels();
- }
+#ifdef G_DEBUG_MODE
+ for (const Event& e : eventBuffer_)
+ G_DEBUG("Event type=" << (int) e.type << ", channel=" << e.action.channelId
+ << ", delta=" << e.delta << ", globalFrame=" << clock::getCurrentFrame());
+#endif
}
/* -------------------------------------------------------------------------- */
-void renderMetronome_(AudioBuffer& outBuf, Frame f)
+void processChannels_(AudioBuffer& out, AudioBuffer& in)
{
- if (!metronome_.running)
- return;
+ model::ChannelsLock lock(model::channels);
- if (clock::isOnBar() || metronome_.playBar)
- metronome_.render(outBuf, metronome_.playBar, metronome_.bar, f);
- else
- if (clock::isOnBeat() || metronome_.playBeat)
- metronome_.render(outBuf, metronome_.playBeat, metronome_.beat, f);
+ for (const Channel* c : model::channels) {
+ bool audible = isChannelAudible_(*c);
+ c->parse(eventBuffer_, audible);
+ if (c->getType() != ChannelType::MASTER) {
+ c->advance(out.countFrames());
+ c->render(&out, &in, audible);
+ }
+ }
}
/* -------------------------------------------------------------------------- */
-void parseEvents_(Frame f)
+void processSequencer_(AudioBuffer& in)
{
- mixer::FrameEvents fe = {
- .frameLocal = f,
- .frameGlobal = clock::getCurrentFrame(),
- .doQuantize = clock::getQuantize() == 0 || !clock::quantoHasPassed(),
- .onBar = clock::isOnBar(),
- .onFirstBeat = clock::isOnFirstBeat(),
- .quantoPassed = clock::quantoHasPassed(),
- .actions = recorder::getActionsOnFrame(clock::getCurrentFrame()),
- };
-
- model::ChannelsLock lock(model::channels);
-
- /* TODO - channel->parseEvents alters things in Channel (i.e. it's mutable).
- Refactoring needed ASAP. */
-
- for (Channel* ch : model::channels)
- ch->parseEvents(fe);
+ sequencer::parse(eventBuffer_);
+ if (clock::isActive()) {
+ if (clock::isRunning())
+ sequencer::run(in.countFrames());
+ lineInRec_(in);
+ }
}
/* -------------------------------------------------------------------------- */
-void render_(AudioBuffer& out, const AudioBuffer& in, AudioBuffer& inToOut)
+void renderMasterIn_(AudioBuffer& in)
{
- bool running = clock::isRunning();
-
model::ChannelsLock lock(model::channels);
-
- /* TODO - channel->render alters things in Channel (i.e. it's mutable).
- Refactoring needed ASAP. */
-
- for (const Channel* ch : model::channels) {
- if (ch == nullptr ||
- ch->id == mixer::MASTER_OUT_CHANNEL_ID ||
- ch->id == mixer::MASTER_IN_CHANNEL_ID)
- continue;
- const_cast<Channel*>(ch)->render(out, in, inToOut, isChannelAudible(ch), running);
- }
-
- assert(model::channels.size() >= 3); // Preview channel included
-
- /* Master channels are processed at the end, when the buffers have already
- been filled. */
-
- model::get(model::channels, mixer::MASTER_OUT_CHANNEL_ID).render(out, in, inToOut, true, true);
- model::get(model::channels, mixer::MASTER_IN_CHANNEL_ID).render(out, in, inToOut, true, true);
+ model::get(model::channels, mixer::MASTER_IN_CHANNEL_ID).render(nullptr, &in, true);
}
-
-/* -------------------------------------------------------------------------- */
-
-
-void processSequencer_(AudioBuffer& out, const AudioBuffer& in)
+void renderMasterOut_(AudioBuffer& out)
{
- for (int j=0; j<out.countFrames(); j++) {
- if (clock::isRunning()) {
- parseEvents_(j);
- doQuantize_(j);
- }
- clock::sendMIDIsync();
- clock::incrCurrentFrame();
- renderMetronome_(out, j);
- }
- lineInRec_(in);
+ model::ChannelsLock lock(model::channels);
+ model::get(model::channels, mixer::MASTER_OUT_CHANNEL_ID).render(&out, nullptr, true);
}
void prepareBuffers_(AudioBuffer& outBuf)
{
outBuf.clear();
- vChanInToOut_.clear();
+ inBuffer_.clear();
}
/* -------------------------------------------------------------------------- */
-/* limitOutput
+/* limit_
Applies a very dumb hard limiter. */
-void limitOutput_(AudioBuffer& outBuf)
+void limit_(AudioBuffer& outBuf)
{
- if (!conf::conf.limitOutput)
- return;
for (int i=0; i<outBuf.countFrames(); i++)
for (int j=0; j<outBuf.countChannels(); j++)
- if (outBuf[i][j] > 1.0f) outBuf[i][j] = 1.0f;
- else if (outBuf[i][j] < -1.0f) outBuf[i][j] = -1.0f;
+ outBuf[i][j] = std::max(-1.0f, std::min(outBuf[i][j], 1.0f));
}
/* finalizeOutput
Last touches after the output has been rendered: apply inToOut if any, apply
-output volume. */
+output volume, compute peak. */
void finalizeOutput_(AudioBuffer& outBuf)
{
- model::MixerLock lock(model::mixer);
-
- //printf("%f\n", mh::getOutVol());
+ bool inToOut = mh::getInToOut();
+ float outVol = mh::getOutVol();
- for (int i=0; i<outBuf.countFrames(); i++)
- for (int j=0; j<outBuf.countChannels(); j++) {
- if (model::mixer.get()->inToOut) // Merge vChanInToOut_, if enabled
- outBuf[i][j] += vChanInToOut_[i][j];
- outBuf[i][j] *= mh::getOutVol();
- }
+ if (inToOut)
+ outBuf.addData(inBuffer_, outVol);
+ else
+ outBuf.applyGain(outVol);
+
+ if (conf::conf.limitOutput)
+ limit_(outBuf);
+
+ peakOut.store(outBuf.getPeak());
}
-}; // {anonymous}
+} // {anonymous}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-std::atomic<bool> rewindWait(false);
std::atomic<float> peakOut(0.0);
std::atomic<float> peakIn(0.0);
+Queue<Event, G_MAX_QUEUE_EVENTS> UIevents;
+Queue<Event, G_MAX_QUEUE_EVENTS> MidiEvents;
+
/* -------------------------------------------------------------------------- */
void init(Frame framesInSeq, Frame framesInBuffer)
{
- /* Allocate virtual inputs. vChanInput_ has variable size: it depends
+ /* Allocate virtual inputs. recBuffer_ has variable size: it depends
on how many frames there are in sequencer. */
- vChanInput_.alloc(framesInSeq, G_MAX_IO_CHANS);
- vChanInToOut_.alloc(framesInBuffer, G_MAX_IO_CHANS);
+ recBuffer_.alloc(framesInSeq, G_MAX_IO_CHANS);
+ inBuffer_.alloc(framesInBuffer, G_MAX_IO_CHANS);
u::log::print("[mixer::init] buffers ready - framesInSeq=%d, framesInBuffer=%d\n",
- framesInSeq, framesInBuffer);
-
- clock::rewind();
+ framesInSeq, framesInBuffer);
}
/* -------------------------------------------------------------------------- */
-void allocVirtualInput(Frame frames)
+void allocRecBuffer(Frame frames)
{
- vChanInput_.alloc(frames, G_MAX_IO_CHANS);
+ recBuffer_.alloc(frames, G_MAX_IO_CHANS);
}
-void clearVirtualInput()
+void clearRecBuffer()
{
- vChanInput_.clear();
+ recBuffer_.clear();
}
-const AudioBuffer& getVirtualInput()
+const AudioBuffer& getRecBuffer()
{
- return vChanInput_;
+ return recBuffer_;
}
int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize,
- double streamTime, RtAudioStreamStatus status, void* userData)
+ double /*streamTime*/, RtAudioStreamStatus /*status*/, void* /*userData*/)
{
if (!kernelAudio::isReady() || active_.load() == false)
return 0;
processing_.store(true);
-#if defined(__linux__) || defined(__FreeBSD__)
-
+#ifdef WITH_AUDIO_JACK
if (kernelAudio::getAPI() == G_SYS_API_JACK)
clock::recvJackSync();
-
#endif
AudioBuffer out, in;
- out.setData((float*) outBuf, bufferSize, G_MAX_IO_CHANS);
+ out.setData(static_cast<float*>(outBuf), bufferSize, G_MAX_IO_CHANS);
if (kernelAudio::isInputEnabled())
- in.setData((float*) inBuf, bufferSize, G_MAX_IO_CHANS);
+ in.setData(static_cast<float*>(inBuf), bufferSize, conf::conf.channelsInCount);
/* Reset peak computation. */
prepareBuffers_(out);
processLineIn_(in);
- /* Process model. */
-
//out[0][0] = 3.0f;
- if (clock::isActive())
- processSequencer_(out, in);
- render_(out, in, vChanInToOut_);
+ renderMasterIn_(inBuffer_);
+
+ fillEventBuffer_();
+ processSequencer_(inBuffer_);
+ processChannels_(out, inBuffer_);
+
+ renderMasterOut_(out);
+
+ /* Advance sequencer only when rendering is done. */
+
+ if (clock::isActive())
+ sequencer::advance(out);
/* Post processing. */
finalizeOutput_(out);
- limitOutput_(out);
- computePeak_(out, peakOut);
/* Unset data in buffers. If you don't do this, buffers go out of scope and
destroy memory allocated by RtAudio ---> havoc. */
+
out.setData(nullptr, 0, 0);
in.setData (nullptr, 0, 0);
/* -------------------------------------------------------------------------- */
-bool isChannelAudible(const Channel* ch)
-{
- model::MixerLock l(model::mixer);
-
- bool hasSolos = model::mixer.get()->hasSolos;
- return !hasSolos || (hasSolos && ch->solo);
-}
-
-bool isMetronomeOn() { return metronome_.running; }
-
-
-/* -------------------------------------------------------------------------- */
-
-
void startInputRec()
{
/* Start inputTracker_ from the current frame, not the beginning. */
/* -------------------------------------------------------------------------- */
-void toggleMetronome()
+void setSignalCallback(std::function<void()> f)
{
- metronome_.running = !metronome_.running;
-}
-
-
-void setMetronome(bool v)
-{
- metronome_.running = v;
+ signalCb_ = f;
}
/* -------------------------------------------------------------------------- */
-void setSignalCallback(std::function<void()> f)
+void pumpEvent(Event e)
{
- signalCb_ = f;
+ eventBuffer_.push_back(e);
}
-}}}; // giada::m::mixer::
+}}} // giada::m::mixer::
#include <functional>
#include <vector>
#include "deps/rtaudio/RtAudio.h"
+#include "core/ringBuffer.h"
#include "core/recorder.h"
#include "core/types.h"
+#include "core/queue.h"
+#include "core/midiEvent.h"
namespace giada {
namespace mixer
{
-struct FrameEvents
+enum class EventType
{
- Frame frameLocal;
- Frame frameGlobal;
- bool doQuantize;
- bool onBar;
- bool onFirstBeat;
- bool quantoPassed;
- const std::vector<Action>* actions;
+ KEY_PRESS,
+ KEY_RELEASE,
+ KEY_KILL,
+ SEQUENCER_FIRST_BEAT, // 3
+ SEQUENCER_BAR, // 4
+ SEQUENCER_START, // 5
+ SEQUENCER_STOP, // 6
+ SEQUENCER_REWIND, // 7
+ SEQUENCER_REWIND_REQ, // 8
+ MIDI,
+ ACTION,
+ CHANNEL_TOGGLE_READ_ACTIONS,
+ CHANNEL_KILL_READ_ACTIONS,
+ CHANNEL_TOGGLE_ARM,
+ CHANNEL_MUTE,
+ CHANNEL_SOLO,
+ CHANNEL_VOLUME,
+ CHANNEL_PITCH,
+ CHANNEL_PAN
};
+struct Event
+{
+ EventType type;
+ Frame delta;
+ Action action;
+};
+
+/* EventBuffer
+Alias for a RingBuffer containing events to be sent to engine. The double size
+is due to the presence of two distinct Queues for collecting events coming from
+other threads. See below. */
+
+using EventBuffer = RingBuffer<Event, G_MAX_QUEUE_EVENTS * 2>;
+
constexpr int MASTER_OUT_CHANNEL_ID = 1;
constexpr int MASTER_IN_CHANNEL_ID = 2;
constexpr int PREVIEW_CHANNEL_ID = 3;
-extern std::atomic<bool> rewindWait; // rewind guard, if quantized
-extern std::atomic<float> peakOut;
-extern std::atomic<float> peakIn;
+extern std::atomic<float> peakOut; // TODO - move to model::
+extern std::atomic<float> peakIn; // TODO - move to model::
+
+/* Channel Event queues
+Collect events coming from the UI or MIDI devices to be sent to channels. Our
+poor's man Queue is a single-producer/single-consumer one, so we need two queues
+for two writers. TODO - let's add a multi-producer queue sooner or later! */
+
+extern Queue<Event, G_MAX_QUEUE_EVENTS> UIevents;
+extern Queue<Event, G_MAX_QUEUE_EVENTS> MidiEvents;
void init(Frame framesInSeq, Frame framesInBuffer);
void enable();
void disable();
-/* allocVirtualInput
+/* allocRecBuffer
Allocates new memory for the virtual input channel. Call this whenever you
shrink or resize the sequencer. */
-void allocVirtualInput(Frame frames);
+void allocRecBuffer(Frame frames);
-/* clearVirtualInput
+/* clearRecBuffer
Clears internal virtual channel. */
-void clearVirtualInput();
+void clearRecBuffer();
-/* getVirtualInput
+/* getRecBuffer
Returns a read-only reference to the internal virtual channel. Use this to
merge data into channel after an input recording session. */
-const AudioBuffer& getVirtualInput();
+const AudioBuffer& getRecBuffer();
void close();
int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize, double streamTime,
RtAudioStreamStatus status, void* userData);
-bool isChannelAudible(const Channel* ch);
-
/* startInputRec, stopInputRec
Starts/stops input recording on frame clock::getCurrentFrame(). */
void startInputRec();
void stopInputRec();
-void toggleMetronome();
-bool isMetronomeOn();
-void setMetronome(bool v);
-
void setSignalCallback(std::function<void()> f);
+
+/* pumpEvent
+Pumps a new mixer::Event into the event vector. Use this function when you want
+to inject a new event for the **current** block. Push the event in the two
+queues UIevents and MIDIevents above if the event can be processed in the next
+block instead. */
+
+void pumpEvent(Event e);
}}} // giada::m::mixer::;
#include "glue/main.h"
#include "glue/channel.h"
#include "core/model/model.h"
-#include "core/channels/channel.h"
-#include "core/channels/sampleChannel.h"
-#include "core/channels/midiChannel.h"
#include "core/channels/channelManager.h"
#include "core/kernelMidi.h"
#include "core/mixer.h"
#include "core/const.h"
#include "core/init.h"
-#include "core/pluginHost.h"
-#include "core/pluginManager.h"
-#include "core/plugin.h"
+#include "core/plugins/pluginHost.h"
+#include "core/plugins/pluginManager.h"
+#include "core/plugins/plugin.h"
#include "core/waveFx.h"
#include "core/conf.h"
#include "core/patch.h"
{
std::unique_ptr<Channel> createChannel_(ChannelType type, ID columnId, ID channelId=0)
{
- std::unique_ptr<Channel> ch = channelManager::create(type,
- kernelAudio::getRealBufSize(), conf::conf.inputMonitorDefaultOn, columnId);
+ std::unique_ptr<Channel> ch = channelManager::create(type, columnId, conf::conf);
if (type == ChannelType::MASTER) {
assert(channelId != 0);
ch->id = channelId;
}
- return ch;
+ return ch;
}
waveManager::Result createWave_(const std::string& fname)
{
- waveManager::Result res = waveManager::createFromFile(fname);
- if (res.status != G_RES_OK)
- return res;
- if (res.wave->getRate() != conf::conf.samplerate) {
- u::log::print("[mh::createWave_] input rate (%d) != system rate (%d), conversion needed\n",
- res.wave->getRate(), conf::conf.samplerate);
- res.status = waveManager::resample(*res.wave.get(), conf::conf.rsmpQuality, conf::conf.samplerate);
- if (res.status != G_RES_OK)
- return res;
- }
- return res;
+ return waveManager::createFromFile(fname, /*ID=*/0, conf::conf.samplerate,
+ conf::conf.rsmpQuality);
}
/* -------------------------------------------------------------------------- */
-bool channelHas_(std::function<bool(const Channel*)> f)
+bool anyChannel_(std::function<bool(const Channel*)> f)
{
model::ChannelsLock lock(model::channels);
return std::any_of(model::channels.begin(), model::channels.end(), f);
/* -------------------------------------------------------------------------- */
-bool canInputRec_(size_t chanIndex)
+template <typename F>
+std::vector<ID> getChannelsIf_(F f)
{
model::ChannelsLock l(model::channels);
- return model::channels.get(chanIndex)->canInputRec();
+
+ std::vector<ID> ids;
+ for (const Channel* c : model::channels)
+ if (f(c)) ids.push_back(c->id);
+
+ return ids;
+}
+
+
+std::vector<ID> getChannelsWithWave_()
+{
+ return getChannelsIf_([] (const Channel* c)
+ {
+ return c->samplePlayer && c->samplePlayer->hasWave();
+ });
+}
+
+
+std::vector<ID> getRecordableChannels_()
+{
+ return getChannelsIf_([] (const Channel* c) { return c->canInputRec() && !c->hasWave(); });
+}
+
+
+std::vector<ID> getOverdubbableChannels_()
+{
+ return getChannelsIf_([] (const Channel* c) { return c->canInputRec() && c->hasWave(); });
}
/* -------------------------------------------------------------------------- */
/* pushWave_
-Pushes a new wave into Sample Channel 'ch' and into the corresponding Wave list.
+Pushes a new wave into Channel 'ch' and into the corresponding Wave list.
Use this when modifying a local model, before swapping it. */
-void pushWave_(SampleChannel& ch, std::unique_ptr<Wave>&& w, bool clone)
+void pushWave_(Channel& ch, std::unique_ptr<Wave>&& w)
{
- if (ch.hasWave && !clone) // Don't pop if cloning a channel
- model::waves.pop(model::getIndex(model::waves, ch.waveId));
-
- ID id = w->id;
- Frame size = w->getSize();
+ assert(ch.getType() == ChannelType::SAMPLE);
model::waves.push(std::move(w));
- ch.pushWave(id, size);
+
+ model::WavesLock l(model::waves);
+ ch.samplePlayer->loadWave(model::waves.back());
}
-}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void setupChannelPostRecording_(Channel& c)
+{
+ /* Start sample channels in loop mode right away. */
+ if (c.samplePlayer->state->isAnyLoopMode())
+ c.samplePlayer->kickIn(clock::getCurrentFrame());
+ /* Disable 'arm' button if overdub protection is on. */
+ if (c.audioReceiver->state->overdubProtection.load() == true)
+ c.state->armed.store(false);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* recordChannel_
+Records the current Mixer audio input data into an empty channel. */
+
+void recordChannel_(ID channelId)
+{
+ /* Create a new Wave with audio coming from Mixer's virtual input. */
+
+ std::string filename = "TAKE-" + std::to_string(patch::patch.lastTakeId++) + ".wav";
+
+ std::unique_ptr<Wave> wave = waveManager::createEmpty(clock::getFramesInLoop(),
+ G_MAX_IO_CHANS, conf::conf.samplerate, filename);
+
+ wave->copyData(mixer::getRecBuffer());
+
+ /* Update Channel with the new Wave. The function pushWave_ will take
+ care of pushing it into the Wave stack first. */
+
+ model::onSwap(model::channels, channelId, [&](Channel& c)
+ {
+ pushWave_(c, std::move(wave));
+ setupChannelPostRecording_(c);
+ });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+/* overdubChannel_
+Records the current Mixer audio input data into a channel with an existing
+Wave, overdub mode. */
+
+void overdubChannel_(ID channelId)
+{
+ ID waveId;
+ model::onGet(model::channels, channelId, [&](Channel& c)
+ {
+ waveId = c.samplePlayer->getWaveId();
+ });
+
+ model::onGet(m::model::waves, waveId, [&](Wave& w)
+ {
+ w.addData(mixer::getRecBuffer());
+ w.setLogical(true);
+ });
+
+ model::onGet(model::channels, channelId, [&](Channel& c)
+ {
+ setupChannelPostRecording_(c);
+ });
+}
+} // {anonymous}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-bool uniqueSamplePath(ID channelToSkip, const std::string& path)
+void addChannel(ChannelType type, ID columnId)
{
- model::ChannelsLock cl(model::channels);
- model::WavesLock wl(model::waves);
-
- for (const Channel* c : model::channels) {
- if (c->id == channelToSkip || c->type != ChannelType::SAMPLE)
- continue;
- const SampleChannel* sc = static_cast<const SampleChannel*>(c);
- if (sc->hasWave && model::get(model::waves, sc->waveId).getPath() == path)
- return false;
- }
- return true;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-ID addChannel(ChannelType type, ID columnId)
-{
- std::unique_ptr<Channel> c = createChannel_(type, columnId);
- ID id = c->id;
- model::channels.push(std::move(c));
- return id;
+ model::channels.push(createChannel_(type, columnId));
}
if (res.status != G_RES_OK)
return res.status;
+ ID oldWaveId;
+
model::onSwap(model::channels, channelId, [&](Channel& c)
{
- pushWave_(static_cast<SampleChannel&>(c), std::move(res.wave), /*clone=*/false);
+ oldWaveId = c.samplePlayer->getWaveId();
+ pushWave_(c, std::move(res.wave));
});
+ /* Remove old wave, if any. It is safe to do it now: the channel already
+ points to the new one. */
+
+ if (oldWaveId != 0)
+ model::waves.pop(model::getIndex(model::waves, oldWaveId));
+
return res.status;
}
void addAndLoadChannel(ID columnId, std::unique_ptr<Wave>&& w)
{
- std::unique_ptr<Channel> ch = createChannel_(ChannelType::SAMPLE,
- columnId);
+ std::unique_ptr<Channel> ch = createChannel_(ChannelType::SAMPLE, columnId);
- pushWave_(static_cast<SampleChannel&>(*ch.get()), std::move(w), /*clone=*/false);
+ pushWave_(*ch.get(), std::move(w));
/* Then add new channel to Channel list. */
/* Clone plugins, actions and wave first in their own lists. */
#ifdef WITH_VST
- pluginHost::clonePlugins(oldChannel, *newChannel.get());
+ newChannel->pluginIds = pluginHost::clonePlugins(oldChannel.pluginIds);
#endif
recorderHandler::cloneActions(channelId, newChannel->id);
- if (newChannel->hasData()) {
- SampleChannel* sch = static_cast<SampleChannel*>(newChannel.get());
- Wave& wave = model::get(model::waves, sch->waveId);
- pushWave_(*sch, waveManager::createFromWave(wave, 0, wave.getSize()), /*clone=*/true);
+ if (newChannel->samplePlayer && newChannel->samplePlayer->hasWave())
+ {
+ Wave& wave = model::get(model::waves, newChannel->samplePlayer->getWaveId());
+ pushWave_(*newChannel, waveManager::createFromWave(wave, 0, wave.getSize()));
}
/* Then push the new channel in the channels list. */
void freeChannel(ID channelId)
{
- bool hasWave;
- ID waveId;
+ ID waveId;
/* Remove Wave reference from Channel. */
model::onSwap(model::channels, channelId, [&](Channel& c)
{
- SampleChannel& sc = static_cast<SampleChannel&>(c);
- hasWave = sc.hasWave;
- waveId = sc.waveId;
- sc.empty();
+ waveId = c.samplePlayer->getWaveId();
+ c.samplePlayer->loadWave(nullptr);
});
/* Then remove the actual Wave, if any. */
- if (hasWave)
+ if (waveId != 0)
model::waves.pop(model::getIndex(model::waves, waveId));
}
void freeAllChannels()
{
- for (size_t i = 0; i < model::channels.size(); i++)
- model::onSwap(model::channels, model::getId(model::channels, i), [](Channel& c) { c.empty(); });
+ for (ID id : getChannelsWithWave_()) {
+ model::onSwap(model::channels, id, [](Channel& c)
+ {
+ c.samplePlayer->loadWave(nullptr);
+ });
+ }
+
model::waves.clear();
}
void deleteChannel(ID channelId)
{
- bool hasWave = false;
ID waveId;
#ifdef WITH_VST
std::vector<ID> pluginIds;
#endif
- model::onGet(model::channels, channelId, [&](Channel& c)
+ model::onGet(model::channels, channelId, [&](const Channel& c)
{
#ifdef WITH_VST
pluginIds = c.pluginIds;
#endif
- if (c.type != ChannelType::SAMPLE)
- return;
- SampleChannel& sc = static_cast<SampleChannel&>(c);
- hasWave = sc.hasWave;
- waveId = sc.waveId;
+ waveId = c.samplePlayer ? c.samplePlayer->getWaveId() : 0;
});
model::channels.pop(model::getIndex(model::channels, channelId));
- if (hasWave)
+ if (waveId != 0)
model::waves.pop(model::getIndex(model::waves, waveId));
#ifdef WITH_VST
void renameChannel(ID channelId, const std::string& name)
{
- model::onSwap(model::channels, channelId, [&](Channel& c) { c.name = name; });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void startSequencer()
-{
- switch (clock::getStatus()) {
- case ClockStatus::STOPPED:
- clock::setStatus(ClockStatus::RUNNING);
- break;
- case ClockStatus::WAITING:
- clock::setStatus(ClockStatus::RUNNING);
- recManager::stopActionRec();
- break;
- default:
- break;
- }
-
-#ifdef __linux__
- kernelAudio::jackStart();
-#endif
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void stopSequencer()
-{
- clock::setStatus(ClockStatus::STOPPED);
-
- /* Stop channels with explicit locks. The RAII version would trigger a
- deadlock if recManager::stopInputRec() is called down below. */
-
- model::channels.lock();
- for (Channel* c : model::channels)
- c->stopBySeq(conf::conf.chansStopOnSeqHalt);
- model::channels.unlock();
-
-#ifdef __linux__
- kernelAudio::jackStop();
-#endif
-
- /* If recordings (both input and action) are active deactivate them, but
- store the takes. RecManager takes care of it. */
-
- if (recManager::isRecordingAction())
- recManager::stopActionRec();
- else
- if (recManager::isRecordingInput())
- recManager::stopInputRec();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void toggleSequencer()
-{
- clock::isRunning() ? stopSequencer() : startSequencer();
+ model::onGet(model::channels, channelId, [&](Channel& c)
+ {
+ c.state->name = name;
+ }, /*rebuild=*/true);
}
void updateSoloCount()
{
- model::onSwap(model::mixer, [&](model::Mixer& m)
+ model::onSwap(model::mixer, [](model::Mixer& m)
{
- m.hasSolos = channelHas_([](const Channel* ch) { return ch->solo; });
+ m.hasSolos = anyChannel_([](const Channel* ch) {
+ return !ch->isInternal() && ch->state->solo.load() == true;
+ });
});
}
/* -------------------------------------------------------------------------- */
-void setInVol(float v)
-{
- model::onGet(model::channels, mixer::MASTER_IN_CHANNEL_ID, [&](Channel& c)
- {
- c.volume = v;
- });
-}
-
-
-void setOutVol(float v)
-{
- model::onGet(model::channels, mixer::MASTER_OUT_CHANNEL_ID, [&](Channel& c)
- {
- c.volume = v;
- });
-}
-
-
void setInToOut(bool v)
{
model::onSwap(model::mixer, [&](model::Mixer& m)
float getInVol()
{
model::ChannelsLock l(model::channels);
- return model::get(model::channels, mixer::MASTER_IN_CHANNEL_ID).volume;
+ return model::get(model::channels, mixer::MASTER_IN_CHANNEL_ID).state->volume.load();
}
float getOutVol()
{
model::ChannelsLock l(model::channels);
- return model::get(model::channels, mixer::MASTER_OUT_CHANNEL_ID).volume;
+ return model::get(model::channels, mixer::MASTER_OUT_CHANNEL_ID).state->volume.load();
}
/* -------------------------------------------------------------------------- */
-
-void rewindSequencer()
-{
- if (clock::getQuantize() > 0 && clock::isRunning()) // quantize rewind
- mixer::rewindWait = true;
- else {
- clock::rewind();
- rewindChannels();
- }
-
- /* FIXME - potential desync when Quantizer is enabled from this point on.
- Mixer would wait, while the following calls would be made regardless of its
- state. */
-
-#ifdef __linux__
- kernelAudio::jackSetPosition(0);
-#endif
-
- if (conf::conf.midiSync == MIDI_SYNC_CLOCK_M)
- kernelMidi::send(MIDI_POSITION_PTR, 0, 0);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void rewindChannels()
-{
- for (size_t i = 3; i < model::channels.size(); i++)
- model::onSwap(model::channels, model::getId(model::channels, i), [&](Channel& c) { c.rewindBySeq(); });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
/* Push a new Wave into each recordable channel. Warning: this algorithm will
require some changes when we will allow overdubbing (the previous existing Wave
has to be overwritten somehow). */
void finalizeInputRec()
{
- const AudioBuffer& virtualInput = mixer::getVirtualInput();
-
- /* Can't loop with foreach, as it would require a lock on model::channels
- list which would deadlock during the model::channels::swap() call below.
- Also skip channels 0, 1 and 2: they are MASTER_IN, MASTER_OUT and PREVIEW. */
+ for (ID id : getRecordableChannels_())
+ recordChannel_(id);
+ for (ID id : getOverdubbableChannels_())
+ overdubChannel_(id);
- for (size_t i = 3; i < model::channels.size(); i++) {
-
- if (!canInputRec_(i))
- continue;
-
- /* Create a new Wave with audio coming from Mixer's virtual input. */
-
- std::string filename = "TAKE-" + std::to_string(patch::patch.lastTakeId++) + ".wav";
-
- std::unique_ptr<Wave> wave = waveManager::createEmpty(clock::getFramesInLoop(),
- G_MAX_IO_CHANS, conf::conf.samplerate, filename);
+ mixer::clearRecBuffer();
+}
- wave->copyData(virtualInput[0], virtualInput.countFrames());
- /* Update Channel with the new Wave. The function pushWave_ will take
- take of pushing it into the stack first. Also start all channels in
- LOOP mode. */
+/* -------------------------------------------------------------------------- */
- model::onSwap(model::channels, model::getId(model::channels, i), [&](Channel& c)
- {
- SampleChannel& sc = static_cast<SampleChannel&>(c);
- pushWave_(sc, std::move(wave), /*clone=*/false);
- if (sc.isAnyLoopMode())
- sc.playStatus = ChannelStatus::PLAY;
- });
- }
- mixer::clearVirtualInput();
+bool hasInputRecordableChannels()
+{
+ return anyChannel_([](const Channel* ch) { return ch->canInputRec(); });
}
-/* -------------------------------------------------------------------------- */
-
-
-bool hasRecordableSampleChannels()
+bool hasActionRecordableChannels()
{
- return channelHas_([](const Channel* ch) { return ch->canInputRec(); });
+ return anyChannel_([](const Channel* ch) { return ch->canActionRec(); });
}
bool hasLogicalSamples()
{
- return channelHas_([](const Channel* ch) { return ch->hasLogicalData(); });
+ return anyChannel_([](const Channel* ch)
+ {
+ return ch->samplePlayer && ch->samplePlayer->hasLogicalWave(); }
+ );
}
bool hasEditedSamples()
{
- return channelHas_([](const Channel* ch) { return ch->hasEditedData(); });
+ return anyChannel_([](const Channel* ch)
+ {
+ return ch->samplePlayer && ch->samplePlayer->hasEditedWave();
+ });
}
bool hasActions()
{
- return channelHas_([](const Channel* ch) { return ch->hasActions; });
+ return anyChannel_([](const Channel* ch) { return ch->state->hasActions; });
}
bool hasAudioData()
{
- return channelHas_([](const Channel* ch) { return ch->hasData(); });
+ return anyChannel_([](const Channel* ch)
+ {
+ return ch->samplePlayer && ch->samplePlayer->hasWave();
+ });
}
-}}}; // giada::m::mh::
+}}} // giada::m::mh::
Adds a new channel of type 'type' into the channels stack. Returns the new
channel ID. */
-ID addChannel(ChannelType type, ID columnId);
+void addChannel(ChannelType type, ID columnId);
/* loadChannel
Loads a new Wave inside a Sample Channel. */
void renameChannel(ID channelId, const std::string& name);
void freeAllChannels();
-void startSequencer();
-void stopSequencer();
-void toggleSequencer();
-void rewindSequencer();
-void rewindChannels();
-
void setInToOut(bool v);
-void setInVol(float f);
-void setOutVol(float f);
/* updateSoloCount
Updates the number of solo-ed channels in mixer. */
void finalizeInputRec();
-/* uniqueSamplePath
-Returns true if path 'p' is unique. Requires SampleChannel 'skip' in order
-to skip check against itself. */
-
-bool uniqueSamplePath(ID channelToSkip, const std::string& p);
-
/* hasLogicalSamples
True if 1 or more samples are logical (memory only, such as takes) */
bool hasEditedSamples();
-/* hasRecordableSampleChannels
-Tells whether Mixer has one or more recordable Sample Channels, that is:
-a) armed; b) empty (no Wave). */
+/* has(Input|Action)RecordableChannels
+Tells whether Mixer has one or more input or action recordable channels. */
-bool hasRecordableSampleChannels();
+bool hasInputRecordableChannels();
+bool hasActionRecordableChannels();
/* hasActions
True if at least one Channel has actions recorded in it. */
#include <cassert>
#include "core/model/model.h"
-#ifndef NDEBUG
-#include "core/channels/channel.h"
-#include "core/channels/sampleChannel.h"
+#ifdef G_DEBUG_MODE
#include "core/channels/channelManager.h"
#endif
RCUList<Recorder> recorder(std::make_unique<Recorder>());
RCUList<MidiIn> midiIn(std::make_unique<MidiIn>());
RCUList<Actions> actions(std::make_unique<Actions>());
-RCUList<Channel> channels;
+RCUList<Channel> channels;
RCUList<Wave> waves;
#ifdef WITH_VST
RCUList<Plugin> plugins;
}
-#ifndef NDEBUG
+#ifdef G_DEBUG_MODE
void debug()
{
int i = 0;
for (const Channel* c : channels) {
- printf(" %d) %p - ID=%d name='%s' columnID=%d\n", i++, (void*)c, c->id, c->name.c_str(), c->columnId);
+ printf("\t%d) %p - ID=%d name='%s' type=%d columnID=%d\n",
+ i++, (void*) c, c->state->id, c->state->name.c_str(), (int) c->getType(), c->getColumnId());
+/*
if (c->hasData())
- printf(" wave: ID=%d\n", static_cast<const SampleChannel*>(c)->waveId);
+ printf("\t\twave: ID=%d\n", static_cast<const SampleChannel*>(c)->waveId);
+*/
#ifdef WITH_VST
if (c->pluginIds.size() > 0) {
- puts(" plugins:");
+ puts("\t\tplugins:");
for (ID id : c->pluginIds)
- printf(" ID=%d\n", id);
+ printf("\t\t\tID=%d\n", id);
}
#endif
}
i = 0;
for (const Wave* w : waves)
- printf(" %d) %p - ID=%d name='%s'\n", i++, (void*)w, w->id, w->getPath().c_str());
+ printf("\t%d) %p - ID=%d name='%s'\n", i++, (void*)w, w->id, w->getPath().c_str());
#ifdef WITH_VST
puts("model::plugins");
i = 0;
for (const Plugin* p : plugins) {
if (p->valid)
- printf(" %d) %p - ID=%d name='%s'\n", i++, (void*)p, p->id, p->getName().c_str());
+ printf("\t%d) %p - ID=%d name='%s'\n", i++, (void*)p, p->id, p->getName().c_str());
else
- printf(" %d) %p - ID=%d INVALID\n", i++, (void*)p, p->id);
+ printf("\t%d) %p - ID=%d INVALID\n", i++, (void*)p, p->id);
}
#endif
puts("model::clock");
- printf(" clock.status = %d\n", static_cast<int>(clock.get()->status));
- printf(" clock.bars = %d\n", clock.get()->bars);
- printf(" clock.beats = %d\n", clock.get()->beats);
- printf(" clock.bpm = %f\n", clock.get()->bpm);
- printf(" clock.quantize = %d\n", clock.get()->quantize);
+ printf("\tclock.status = %d\n", static_cast<int>(clock.get()->status));
+ printf("\tclock.bars = %d\n", clock.get()->bars);
+ printf("\tclock.beats = %d\n", clock.get()->beats);
+ printf("\tclock.bpm = %f\n", clock.get()->bpm);
+ printf("\tclock.quantize = %d\n", clock.get()->quantize);
puts("model::actions");
for (auto& kv : actions.get()->map) {
- printf(" frame: %d\n", kv.first);
+ printf("\tframe: %d\n", kv.first);
for (const Action& a : kv.second)
- printf(" (%p) - ID=%d, frame=%d, channel=%d, value=0x%X, prevId=%d, prev=%p, nextId=%d, next=%p\n",
+ printf("\t\t(%p) - ID=%d, frame=%d, channel=%d, value=0x%X, prevId=%d, prev=%p, nextId=%d, next=%p\n",
(void*) &a, a.id, a.frame, a.channelId, a.event.getRaw(), a.prevId, (void*) a.prev, a.nextId, (void*) a.next);
}
puts("===============================");
}
-#endif
+#endif // G_DEBUG_MODE
}}} // giada::m::model::
#include <algorithm>
-#include <type_traits>
+#include "core/model/traits.h"
#include "core/channels/channel.h"
+#include "core/channels/state.h"
#include "core/const.h"
#include "core/wave.h"
-#include "core/plugin.h"
+#include "core/plugins/plugin.h"
#include "core/rcuList.h"
#include "core/recorder.h"
namespace m {
namespace model
{
+namespace
+{
+/* getIter_
+Returns an iterator of an element from list 'list' with given ID. */
+
+template<typename L>
+auto getIter_(L& list, ID id)
+{
+ static_assert(has_id<typename L::value_type>(), "This type has no ID");
+ auto it = std::find_if(list.begin(), list.end(), [&](auto* t)
+ {
+ return t->id == id;
+ });
+ assert(it != list.end());
+ return it;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+/* onSwapByIndex_
+Swaps i-th element from list with a new one and applies a function f to it. */
+
+template<typename L>
+void onSwapByIndex_(L& list, std::size_t i, std::function<void(typename L::value_type&)> f)
+{
+ std::unique_ptr<typename L::value_type> o = list.clone(i);
+ f(*o.get());
+ list.swap(std::move(o), i);
+}
+} // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
struct Clock
{
ClockStatus status = ClockStatus::STOPPED;
#endif
-/* ---------------------------------------------------------------------------*/
-
-
-template <typename T> struct has_id : std::false_type {};
-template <> struct has_id<Channel> : std::true_type {};
-template <> struct has_id<Wave> : std::true_type {};
-#ifdef WITH_VST
-template <> struct has_id<Plugin> : std::true_type {};
-#endif
-
-template <typename T> struct is_copyable : std::true_type {};
-template <> struct is_copyable<Channel> : std::false_type {};
-
-
/* -------------------------------------------------------------------------- */
template<typename L>
-auto getIter(L& list, ID id)
+bool exists(L& list, ID id)
{
- static_assert(has_id<typename L::value_type>(), "This type has no ID");
+ static_assert(has_id<typename L::value_type>(), "This type has no ID");
+ typename L::Lock l(list);
auto it = std::find_if(list.begin(), list.end(), [&](auto* t)
{
return t->id == id;
});
- assert(it != list.end());
- return it;
+ return it != list.end();
}
/* -------------------------------------------------------------------------- */
+/* getIndex (thread safe)
+Returns the index of element with ID from a list. */
+
template<typename L>
-size_t getIndex(L& list, ID id)
+std::size_t getIndex(L& list, ID id)
{
static_assert(has_id<typename L::value_type>(), "This type has no ID");
typename L::Lock l(list);
- return std::distance(list.begin(), getIter(list, id));
+ return std::distance(list.begin(), getIter_(list, id));
}
/* -------------------------------------------------------------------------- */
+/* getIndex (thread safe)
+Returns the element ID of the i-th element of a list. */
+
template<typename L>
-ID getId(L& list, size_t i)
+ID getId(L& list, std::size_t i)
{
static_assert(has_id<typename L::value_type>(), "This type has no ID");
typename L::Lock l(list);
typename L::value_type& get(L& list, ID id)
{
static_assert(has_id<typename L::value_type>(), "This type has no ID");
- return **getIter(list, id);
+ return **getIter_(list, id);
}
/* -------------------------------------------------------------------------- */
-/* onGet (1)
+/* onGet (1) (thread safe)
Utility function for reading ID-based things from a RCUList. */
template<typename L>
-void onGet(L& list, ID id, std::function<void(typename L::value_type&)> f)
+void onGet(L& list, ID id, std::function<void(typename L::value_type&)> f, bool rebuild=false)
{
static_assert(has_id<typename L::value_type>(), "This type has no ID");
typename L::Lock l(list);
- f(**getIter(list, id));
+ f(**getIter_(list, id));
+ if (rebuild)
+ list.changed.store(true);
}
-/* onGet (2)
+/* onGet (2) (thread safe)
Same as (1), for non-ID-based things. */
template<typename L>
/* ---------------------------------------------------------------------------*/
-template<typename L>
-void onSwapByIndex_(L& list, size_t i, std::function<void(typename L::value_type&)> f)
-{
- std::unique_ptr<typename L::value_type> o = list.clone(i);
- f(*o.get());
- list.swap(std::move(o), i);
-}
-
-/* onSwapById_ (1)
-Regular version for copyable types. */
-
-template<typename L>
-void onSwapById_(L& list, ID id, std::function<void(typename L::value_type&)> f,
- const std::true_type& /*is_copyable=true*/)
-{
- static_assert(has_id<typename L::value_type>(), "This type has no ID");
- onSwapByIndex_(list, getIndex(list, id), f);
-}
-
-
-/* onSwapById_ (2)
-Custom version for non-copyable types, e.g. Channel types. Let's wait for the
-no-virtual channel refactoring... */
-
-template<typename L>
-void onSwapById_(L& list, ID id, std::function<void(typename L::value_type&)> f,
- const std::false_type& /*is_copyable=false*/)
-{
- static_assert(has_id<typename L::value_type>(), "This type has no ID");
-
- size_t i = getIndex(list, id);
-
- list.lock();
- std::unique_ptr<typename L::value_type> o(list.get(i)->clone());
- list.unlock();
-
- f(*o.get());
-
- channels.swap(std::move(o), i);
-}
-
-
-/* onSwap (1)
-Utility function for swapping things in a RCUList. */
+/* onSwap (1) (thread safe)
+Utility function for swapping ID-based things in a RCUList. */
template<typename L>
void onSwap(L& list, ID id, std::function<void(typename L::value_type&)> f)
{
static_assert(has_id<typename L::value_type>(), "This type has no ID");
- onSwapById_(list, id, f, is_copyable<typename L::value_type>());
+ onSwapByIndex_(list, getIndex(list, id), f);
}
-/* onSwap (2)
+/* onSwap (2) (thread safe)
Utility function for swapping things in a RCUList when the list contains only
a single element (and so with no ID). */
}
-/* ---------------------------------------------------------------------------*/
+/* ---------------------------------------------------------------------------*/
-#ifndef NDEBUG
+#ifdef G_DEBUG_MODE
void debug();
#include <cassert>
#include "core/model/model.h"
#include "core/channels/channelManager.h"
-#include "core/channels/sampleChannel.h"
-#include "core/channels/midiChannel.h"
#include "core/kernelAudio.h"
#include "core/patch.h"
#include "core/conf.h"
-#include "core/pluginManager.h"
+#include "core/plugins/pluginManager.h"
#include "core/recorderHandler.h"
#include "core/waveManager.h"
+#include "core/sequencer.h"
#include "core/model/storage.h"
patch.beats = clock.get()->beats;
patch.bpm = clock.get()->bpm;
patch.quantize = clock.get()->quantize;
- patch.metronome = mixer::isMetronomeOn(); // TODO - not here
+ patch.metronome = sequencer::isMetronomeOn();
+ patch.samplerate = conf::conf.samplerate;
#ifdef WITH_VST
for (const Plugin* p : plugins)
onSwap(actions, [&](Actions& a)
{
- a.map = std::move(recorderHandler::deserializeActions(patch.actions));
+ a.map = recorderHandler::deserializeActions(patch.actions);
});
+
#ifdef WITH_VST
for (const patch::Plugin& pplugin : patch.plugins)
- plugins.push(pluginManager::deserializePlugin(pplugin));
+ plugins.push(pluginManager::deserializePlugin(pplugin, patch.version));
#endif
- for (const patch::Wave& pwave : patch.waves)
- waves.push(std::move(waveManager::deserializeWave(pwave)));
-
- for (const patch::Channel& pchannel : patch.channels) {
- if (pchannel.type == ChannelType::MASTER || pchannel.type == ChannelType::PREVIEW)
- onSwap(channels, pchannel.id, [&](Channel& ch) { ch.load(pchannel); });
+ for (const patch::Wave& pwave : patch.waves) {
+ std::unique_ptr<Wave> w = waveManager::deserializeWave(pwave, conf::conf.samplerate,
+ conf::conf.rsmpQuality);
+ if (w != nullptr)
+ waves.push(std::move(w));
+ }
+
+ channels.clear();
+ for (const patch::Channel& pchannel : patch.channels)
+ channels.push(channelManager::deserializeChannel(pchannel, kernelAudio::getRealBufSize()));
+
+ /* Load Waves into Channels. */
+
+ ChannelsLock cl(channels);
+ WavesLock wl(waves);
+
+ float samplerateRatio = conf::conf.samplerate / static_cast<float>(patch::patch.samplerate);
+
+ for (Channel* c : channels) {
+ if (!c->samplePlayer)
+ continue;
+ if (exists(waves, c->samplePlayer->getWaveId()))
+ c->samplePlayer->setWave(get(waves, c->samplePlayer->getWaveId()), samplerateRatio);
else
- channels.push(channelManager::deserializeChannel(pchannel, kernelAudio::getRealBufSize()));
- }
+ c->samplePlayer->setInvalidWave();
+ }
}
onSwap(midiIn, [&](MidiIn& m)
{
m.enabled = c.midiInEnabled;
- m.filter = c.midiInFilter;
+ m.filter = c.midiInFilter;
m.rewind = c.midiInRewind;
m.startStop = c.midiInStartStop;
m.actionRec = c.midiInActionRec;
m.inputRec = c.midiInInputRec;
- m.volumeIn = c.midiInMetronome;
- m.volumeOut = c.midiInVolumeIn;
- m.beatDouble = c.midiInVolumeOut;
- m.beatHalf = c.midiInBeatDouble;
- m.metronome = c.midiInBeatHalf;
+ m.volumeIn = c.midiInVolumeIn;
+ m.volumeOut = c.midiInVolumeOut;
+ m.beatDouble = c.midiInBeatDouble;
+ m.beatHalf = c.midiInBeatHalf;
+ m.metronome = c.midiInMetronome;
});
}
}}} // giada::m::model::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_MODEL_TRAITS_H
+#define G_MODEL_TRAITS_H
+
+
+#include <type_traits>
+#include "core/wave.h"
+#include "core/plugins/plugin.h"
+#include "core/channels/channel.h"
+
+
+namespace giada {
+namespace m {
+namespace model
+{
+template <typename T> struct has_id : std::false_type {};
+template <> struct has_id<Channel> : std::true_type {};
+template <> struct has_id<Wave> : std::true_type {};
+#ifdef WITH_VST
+template <> struct has_id<Plugin> : std::true_type {};
+#endif
+}}} // giada::m::model::
+
+
+#endif
p.path = jplugin.value(PATCH_KEY_PLUGIN_PATH, "");
p.bypass = jplugin.value(PATCH_KEY_PLUGIN_BYPASS, false);
- for (const auto& jparam : jplugin[PATCH_KEY_PLUGIN_PARAMS])
- p.params.push_back(jparam);
+ if (patch.version < 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);
if (!j.contains(PATCH_KEY_CHANNELS))
return;
- ID id = mixer::PREVIEW_CHANNEL_ID;
+ ID defaultId = mixer::PREVIEW_CHANNEL_ID;
for (const auto& jchannel : j[PATCH_KEY_CHANNELS]) {
Channel c;
- c.id = jchannel.value(PATCH_KEY_CHANNEL_ID, ++id);
+ c.id = jchannel.value(PATCH_KEY_CHANNEL_ID, ++defaultId);
c.type = static_cast<ChannelType>(jchannel.value(PATCH_KEY_CHANNEL_TYPE, 1));
c.volume = jchannel.value(PATCH_KEY_CHANNEL_VOLUME, G_DEFAULT_VOL);
c.height = jchannel.value(PATCH_KEY_CHANNEL_SIZE, G_GUI_UNIT);
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.5);
+ 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.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<ChannelMode>(jchannel.value(PATCH_KEY_CHANNEL_MODE, 1));
+ c.mode = static_cast<SamplePlayerMode>(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.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);
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_PARAMS] = nl::json::array();
- for (float p : p.params)
- jplugin[PATCH_KEY_PLUGIN_PARAMS].push_back(p);
+ jplugin[PATCH_KEY_PLUGIN_STATE] = p.state;
jplugin[PATCH_KEY_PLUGIN_MIDI_IN_PARAMS] = nl::json::array();
- for (uint32_t p : p.midiInParams)
- jplugin[PATCH_KEY_PLUGIN_MIDI_IN_PARAMS].push_back(p);
+ for (uint32_t param : p.midiInParams)
+ jplugin[PATCH_KEY_PLUGIN_MIDI_IN_PARAMS].push_back(param);
j[PATCH_KEY_PLUGINS].push_back(jplugin);
}
/* -------------------------------------------------------------------------- */
-void writeCommons_(nl::json& j, const std::string& name)
+void writeCommons_(nl::json& j)
{
j[PATCH_KEY_HEADER] = "GIADAPTC";
j[PATCH_KEY_VERSION_MAJOR] = G_VERSION_MAJOR;
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;
j[PATCH_KEY_CHANNELS].push_back(jchannel);
}
}
-}; // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void modernize_()
+{
+ for (Channel& c : patch.channels) {
+ /* 0.16.3
+ Make sure that ChannelType is correct: ID 1, 2 are MASTER channels, ID 3
+ is PREVIEW channel. */
+ if (c.id == mixer::MASTER_OUT_CHANNEL_ID || c.id == mixer::MASTER_IN_CHANNEL_ID)
+ c.type = ChannelType::MASTER;
+ else
+ if (c.id == mixer::PREVIEW_CHANNEL_ID)
+ 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;
+ }
+ }
+}
+} // {anonymous}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-bool write(const std::string& name, const std::string& file)
+bool write(const std::string& file)
{
nl::json j;
- writeCommons_(j, name);
+ writeCommons_(j);
writeColumns_(j);
writeChannels_(j);
writeActions_(j);
if (j[PATCH_KEY_HEADER] != "GIADAPTC")
return G_PATCH_INVALID;
- Version version = {
+ patch.version = {
static_cast<int>(j[PATCH_KEY_VERSION_MAJOR]),
static_cast<int>(j[PATCH_KEY_VERSION_MINOR]),
static_cast<int>(j[PATCH_KEY_VERSION_PATCH])
};
- if (version < Version{0, 16, 0})
+ if (patch.version < Version{0, 16, 0})
return G_PATCH_UNSUPPORTED;
try {
readWaves_(j, basePath);
readActions_(j);
readChannels_(j);
+ modernize_();
}
catch (nl::json::exception& e) {
u::log::print("[patch::read] Exception thrown: %s\n", e.what());
return G_PATCH_OK;
}
-}}}; // giada::m::patch::
+}}} // giada::m::patch::
bool mute;
bool solo;
float volume = G_DEFAULT_VOL;
- float pan = 0.5f;
+ float pan = G_DEFAULT_PAN;
bool hasActions;
bool armed;
bool midiIn;
uint32_t midiOutLmute;
uint32_t midiOutLsolo;
// sample channel
- ID waveId;
- ChannelMode mode;
- Frame begin;
- Frame end;
- Frame shift;
- bool readActions;
- float pitch = G_DEFAULT_PITCH;
- bool inputMonitor;
- bool midiInVeloAsVol;
- uint32_t midiInReadActions;
- uint32_t midiInPitch;
+ 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;
ID id;
std::string path;
bool bypass;
- std::vector<float> params;
+ std::vector<float> params; // TODO - to be removed in 0.18.0
+ std::string state;
std::vector<uint32_t> midiInParams;
};
#endif
struct Patch
{
+ Version version;
std::string name = G_DEFAULT_PATCH_NAME;
int bars = G_DEFAULT_BARS;
int beats = G_DEFAULT_BEATS;
/* write
Writes patch to file. */
-bool write(const std::string& name, const std::string& file);
-}}}; // giada::m::patch::
+bool write(const std::string& file);
+}}} // giada::m::patch::
#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-
-#include <cassert>
-#include <FL/Fl.H>
-#include "utils/log.h"
-#include "utils/time.h"
-#include "const.h"
-#include "plugin.h"
-
-
-using std::string;
-
-
-namespace giada {
-namespace m
-{
-Plugin::Plugin(ID id, const std::string& UID)
-: id (id),
- valid (false),
- m_plugin(nullptr),
- m_UID (UID)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-Plugin::Plugin(ID id, juce::AudioPluginInstance* plugin, double samplerate,
- int buffersize)
-: id (id),
- valid (true),
- m_plugin (plugin),
- m_bypass (false)
-{
- /* Initialize midiInParams vector, where midiInParams.size == number of
- plugin parameters. All values are initially empty (0x0): they will be filled
- during MIDI learning process. */
-
- midiInParams = std::vector<uint32_t>(m_plugin->getParameters().size());
-
- 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);
-
- m_plugin->prepareToPlay(samplerate, buffersize);
-
- u::log::print("[Plugin] plugin initialized and ready. MIDI input params: %lu\n",
- midiInParams.size());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-Plugin::Plugin(const Plugin& o)
-: id (o.id),
- valid (o.valid),
- m_plugin (o.m_plugin),
- m_bypass (o.m_bypass.load()),
- midiInParams(o.midiInParams)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-Plugin::~Plugin()
-{
- if (!valid)
- return;
- m_plugin->suspendProcessing(true);
- m_plugin->releaseResources();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-juce::AudioProcessor::Bus* Plugin::getMainBus(BusType b) const
-{
- const bool isInput = static_cast<bool>(b);
- for (int i=0; i<m_plugin->getBusCount(isInput); i++)
- 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
-{
- return m_plugin->createEditorIfNeeded();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string Plugin::getUniqueId() const
-{
- if (!valid)
- return m_UID;
- return m_plugin->getPluginDescription().createIdentifierString().toStdString();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Plugin::getNumParameters() const
-{
- return valid ? m_plugin->getParameters().size() : 0;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-float Plugin::getParameter(int paramIndex) const
-{
- return m_plugin->getParameters()[paramIndex]->getValue();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Plugin::setParameter(int paramIndex, float value) const
-{
- m_plugin->getParameters()[paramIndex]->setValue(value);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string Plugin::getName() const
-{
- return m_plugin->getName().toStdString();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool Plugin::isSuspended() const
-{
- return m_plugin->isSuspended();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool Plugin::acceptsMidi() const
-{
- return m_plugin->acceptsMidi();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool Plugin::isBypassed() const { return m_bypass.load(); }
-void Plugin::setBypass(bool b) { m_bypass.store(b); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Plugin::process(juce::AudioBuffer<float>& out, juce::MidiBuffer m)
-{
- /* If this is not an instrument (i.e. doesn't accept MIDI), copy the
- incoming buffer data into the temporary one. This way FXes will process
- existing audio data. Conversely, if the plug-in is an instrument, it
- generates its own audio data inside a clean m_buffer and we can play more
- than one plug-in instrument in the same stack, driven by the same set of
- MIDI events. */
-
- const bool isInstrument = m_plugin->acceptsMidi();
-
- if (!isInstrument)
- m_buffer = out;
- else
- m_buffer.clear();
-
- m_plugin->processBlock(m_buffer, m);
-
- /* The local buffer is now filled. Let's try to fill the 'out' one as well
- by taking into account the bus layout - many plug-ins might have mono output
- and we have a stereo buffer to fill. */
-
- for (int i=0, j=0; i<out.getNumChannels(); i++) {
- if (isInstrument)
- out.addFrom(i, 0, m_buffer, j, 0, m_buffer.getNumSamples());
- else
- out.copyFrom(i, 0, m_buffer, j, 0, m_buffer.getNumSamples());
- if (i < countMainOutChannels() - 1)
- j++;
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Plugin::getNumPrograms() const
-{
- return m_plugin->getNumPrograms();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int Plugin::getCurrentProgram() const
-{
- return m_plugin->getCurrentProgram();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void Plugin::setCurrentProgram(int index) const
-{
- m_plugin->setCurrentProgram(index);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool Plugin::hasEditor() const
-{
- return m_plugin->hasEditor();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string Plugin::getProgramName(int index) const
-{
- return m_plugin->getProgramName(index).toStdString();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string Plugin::getParameterName(int index) const
-{
- const int labelSize = 64;
- return m_plugin->getParameters()[index]->getName(labelSize).toStdString();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string Plugin::getParameterText(int index) const
-{
- return m_plugin->getParameters()[index]->getCurrentValueAsText().toStdString();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-string Plugin::getParameterLabel(int index) const
-{
- return m_plugin->getParameters()[index]->getLabel().toStdString();
-}
-
-}} // giada::m::
-
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-#ifndef G_PLUGIN_H
-#define G_PLUGIN_H
-
-
-#include <deque>
-#include "deps/juce-config.h"
-#include "pluginHost.h"
-#include "const.h"
-
-
-namespace giada {
-namespace m
-{
-class Plugin
-{
-public:
-
- Plugin(ID id, const std::string& UID);
- Plugin(ID id, juce::AudioPluginInstance* p, double samplerate, int buffersize);
- Plugin(const Plugin& o);
- ~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;
- int getNumPrograms() const;
- int getCurrentProgram() const;
- std::string getProgramName(int index) const;
- void setParameter(int index, float value) const;
- void setCurrentProgram(int index) const;
- bool acceptsMidi() const;
-
- juce::AudioProcessorEditor* createEditor() const;
-
- /* process
- Process the plug-in with audio and MIDI data. The audio buffer is a reference:
- it has to be altered by the plug-in itself. Conversely, 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. */
-
- void process(juce::AudioBuffer<float>& b, juce::MidiBuffer m);
-
- void setBypass(bool b);
-
- /* id
- Unique identifier. */
-
- ID id;
-
- /* midiInParams
- A vector of midiIn hex values for parameter automation. */
-
- std::vector<uint32_t> midiInParams;
-
- /* valid
- A missing plug-in is loaded anyway, yet marked as 'invalid'. */
-
- bool valid;
-
-private:
-
-#ifdef G_OS_WINDOWS
- /* Fuck... */
- #undef IN
- #undef OUT
-#endif
-
- enum class BusType
- {
- IN = true, OUT = false
- };
-
- juce::AudioProcessor::Bus* getMainBus(BusType b) const;
-
- /* countMainOutChannels
- Returns the current channel layout for the main output bus. */
-
- int countMainOutChannels() const;
-
- juce::AudioPluginInstance* m_plugin;
- juce::AudioBuffer<float> m_buffer;
-
- std::atomic<bool> m_bypass;
-
- /* UID
- The original UID, used for missing plugins. */
-
- std::string m_UID;
-};
-}} // giada::m::
-
-#endif
-
-#endif // #ifdef WITH_VST
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-#include <cassert>
-#include "utils/log.h"
-#include "utils/vector.h"
-#include "core/model/model.h"
-#include "core/channels/channel.h"
-#include "core/const.h"
-#include "core/plugin.h"
-#include "core/pluginManager.h"
-#include "core/pluginHost.h"
-
-
-namespace giada {
-namespace m {
-namespace pluginHost
-{
-namespace
-{
-juce::MessageManager* messageManager_;
-juce::AudioBuffer<float> audioBuffer_;
-ID pluginId_;
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void giadaToJuceTempBuf_(const AudioBuffer& outBuf)
-{
- for (int i=0; i<outBuf.countFrames(); i++)
- for (int j=0; j<outBuf.countChannels(); j++)
- audioBuffer_.setSample(j, i, outBuf[i][j]);
-}
-
-
-/* juceToGiadaOutBuf_
-Converts buffer from Juce to Giada. A note for the future: if we overwrite (=)
-(as we do now) it's SEND, if we add (+) it's INSERT. */
-
-void juceToGiadaOutBuf_(AudioBuffer& outBuf)
-{
- for (int i=0; i<outBuf.countFrames(); i++)
- for (int j=0; j<outBuf.countChannels(); j++)
- outBuf[i][j] = audioBuffer_.getSample(j, i);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void processPlugins_(const std::vector<ID>& pluginIds, juce::MidiBuffer& events)
-{
- model::PluginsLock l(model::plugins);
-
- for (ID id : pluginIds) {
- Plugin& p = model::get(model::plugins, id);
- if (!p.valid || p.isSuspended() || p.isBypassed())
- continue;
- p.process(audioBuffer_, events);
- events.clear();
- }
-}
-
-
-ID clonePlugin_(ID pluginId)
-{
- model::PluginsLock l(model::plugins);
-
- const Plugin& original = model::get(model::plugins, pluginId);
- std::unique_ptr<Plugin> clone = pluginManager::makePlugin(original);
- ID newId = clone->id;
-
- model::plugins.push(std::move(clone));
-
- return newId;
-}
-}; // {anonymous}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-void close()
-{
- messageManager_->deleteInstance();
- model::plugins.clear();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void init(int buffersize)
-{
- messageManager_ = juce::MessageManager::getInstance();
- audioBuffer_.setSize(G_MAX_IO_CHANS, buffersize);
- pluginId_ = 0;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void processStack(AudioBuffer& outBuf, const std::vector<ID>& pluginIds,
- juce::MidiBuffer* events)
-{
- assert(outBuf.countFrames() == audioBuffer_.getNumSamples());
-
- /* If events are null: Audio stack processing (master in, master out or
- sample channels. No need for MIDI events.
- If events are not null: MIDI stack (MIDI channels). MIDI channels must not
- process the current buffer: give them an empty and clean one. */
-
- if (events == nullptr) {
- giadaToJuceTempBuf_(outBuf);
- juce::MidiBuffer events; // empty
- processPlugins_(pluginIds, events);
- }
- else {
- audioBuffer_.clear();
- processPlugins_(pluginIds, *events);
-
- }
- juceToGiadaOutBuf_(outBuf);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void addPlugin(std::unique_ptr<Plugin> p, ID channelId)
-{
- ID pluginId = p->id;
-
- model::plugins.push(std::move(p));
-
- model::onSwap(model::channels, channelId, [&](Channel& c)
- {
- c.pluginIds.push_back(pluginId);
- });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void swapPlugin(ID pluginId1, ID pluginId2, ID channelId)
-{
- model::onSwap(model::channels, channelId, [&](Channel& c)
- {
- auto a = u::vector::indexOf(c.pluginIds, pluginId1);
- auto b = u::vector::indexOf(c.pluginIds, pluginId2);
-
- std::swap(c.pluginIds.at(a), c.pluginIds.at(b));
- });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void freePlugin(ID pluginId, ID channelId)
-{
- model::onSwap(model::channels, channelId, [&](Channel& c)
- {
- u::vector::remove(c.pluginIds, pluginId);
- });
-
- model::plugins.pop(model::getIndex(model::plugins, pluginId));
-}
-
-
-void freePlugins(const std::vector<ID>& pluginIds)
-{
- for (ID id : pluginIds)
- model::plugins.pop(model::getIndex(model::plugins, id));
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void clonePlugins(const Channel& oldChannel, Channel& newChannel)
-{
- newChannel.pluginIds.clear();
- for (ID id : oldChannel.pluginIds)
- newChannel.pluginIds.push_back(clonePlugin_(id));
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setPluginParameter(ID pluginId, int paramIndex, float value)
-{
- model::onGet(model::plugins, pluginId, [&](Plugin& p)
- {
- p.setParameter(paramIndex, value);
- });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setPluginProgram(ID pluginId, int programIndex)
-{
- model::onGet(model::plugins, pluginId, [&](Plugin& p)
- {
- p.setCurrentProgram(programIndex);
- });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void toggleBypass(ID pluginId)
-{
- model::onGet(model::plugins, pluginId, [&](Plugin& p)
- {
- p.setBypass(!p.isBypassed());
- });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void runDispatchLoop()
-{
- messageManager_->runDispatchLoopUntil(10);
-}
-
-}}}; // giada::m::pluginHost::
-
-
-#endif // #ifdef WITH_VST
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-
-#ifndef G_PLUGIN_HOST_H
-#define G_PLUGIN_HOST_H
-
-
-#include <functional>
-#include "deps/juce-config.h"
-#include "core/types.h"
-
-
-namespace giada {
-namespace m
-{
-class Plugin;
-class Channel;
-class AudioBuffer;
-
-namespace pluginHost
-{
-using Stack = std::vector<std::shared_ptr<Plugin>>;
-
-void init(int buffersize);
-void close();
-
-/* addPlugin
-Adds a new plugin to channel 'channelId'. */
-
-void addPlugin(std::unique_ptr<Plugin> p, ID channelId);
-
-/* processStack
-Applies the fx list to the buffer. */
-
-void processStack(AudioBuffer& outBuf, const std::vector<ID>& pluginIds,
- juce::MidiBuffer* events=nullptr);
-
-/* swapPlugin
-Swaps plug-in with ID 1 with plug-in with ID 2 in Channel 'channelId'. */
-
-void swapPlugin(ID pluginId1, ID pluginId2, ID channelId);
-
-/* freePlugin.
-Unloads plugin from channel 'channelId'. */
-
-void freePlugin(ID pluginId, ID channelId);
-
-/* freePlugins
-Unloads multiple plugins. Useful when freeing or deleting a channel. */
-
-void freePlugins(const std::vector<ID>& pluginIds);
-
-/* clonePlugins
-Clones all the plug-ins from the current channel to the new one. */
-
-void clonePlugins(const Channel& oldChannel, Channel& newChannel);
-
-void setPluginParameter(ID pluginId, int paramIndex, float value);
-
-void setPluginProgram(ID pluginId, int programIndex);
-
-void toggleBypass(ID pluginId);
-
-/* runDispatchLoop
-Wakes up plugins' GUI manager for N milliseconds. */
-
-void runDispatchLoop();
-}}}; // giada::m::pluginHost::
-
-
-#endif
-
-#endif // #ifdef WITH_VST
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-
-#include <cassert>
-#include "utils/log.h"
-#include "utils/fs.h"
-#include "utils/string.h"
-#include "core/const.h"
-#include "core/idManager.h"
-#include "core/patch.h"
-#include "core/conf.h"
-#include "core/plugin.h"
-#include "pluginManager.h"
-
-
-namespace giada {
-namespace m {
-namespace pluginManager
-{
-namespace
-{
-IdManager pluginId_;
-
-int samplerate_;
-int buffersize_;
-
-/* pluginFormat
-Plugin format manager. */
-
-juce::VSTPluginFormat pluginFormat_;
-
-/* knownPuginList
-List of known (i.e. scanned) plugins. */
-
-juce::KnownPluginList knownPluginList_;
-
-/* unknownPluginList
-List of unrecognized plugins found in a patch. */
-
-std::vector<std::string> unknownPluginList_;
-
-/* missingPlugins
-If some plugins from any stack are missing. */
-
-bool missingPlugins_;
-
-std::vector<std::string> splitPluginDescription_(const std::string& descr)
-{
- // input: VST-mda-Ambience-18fae2d2-6d646141 string
- // output: [2-------------] [1-----] [0-----] vector.size() == 3
-
- std::vector<std::string> out;
-
- std::string chunk = "";
- int count = 2;
- for (int i=descr.length()-1; i >= 0; i--) {
- if (descr[i] == '-' && count != 0) {
- out.push_back(chunk);
- count--;
- chunk = "";
- }
- else
- chunk += descr[i];
- }
- out.push_back(chunk);
-
- return out;
-}
-
-
-/* findPluginDescription
-Browses the list of known plug-ins until plug-in with id == 'id' is found.
-Unfortunately knownPluginList_.getTypeForIdentifierString(id) doesn't work for
-VSTs: their ID is based on the plug-in file location. E.g.:
-
- /home/vst/mdaAmbience.so -> VST-mdaAmbience-18fae2d2-6d646141
- /home/vst-test/mdaAmbience.so -> VST-mdaAmbience-b328b2f6-6d646141
-
-The following function simply drops the first hash code during comparison. */
-
-const juce::PluginDescription* findPluginDescription_(const std::string& id)
-{
- std::vector<std::string> idParts = splitPluginDescription_(id);
-
- for (const juce::PluginDescription* pd : knownPluginList_) {
- std::vector<std::string> tmpIdParts = splitPluginDescription_(pd->createIdentifierString().toStdString());
- if (idParts[0] == tmpIdParts[0] && idParts[2] == tmpIdParts[2])
- return pd;
- }
- return nullptr;
-}
-}; // {anonymous}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
-void init(int samplerate, int buffersize)
-{
- pluginId_ = IdManager();
- samplerate_ = samplerate;
- buffersize_ = buffersize;
- missingPlugins_ = false;
- unknownPluginList_.clear();
- loadList(u::fs::getHomePath() + G_SLASH + "plugins.xml");
- sortPlugins(static_cast<pluginManager::SortMethod>(conf::conf.pluginSortMethod));
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int scanDirs(const std::string& dirs, const std::function<void(float)>& cb)
-{
- u::log::print("[pluginManager::scanDir] requested directories: '%s'\n", dirs.c_str());
- u::log::print("[pluginManager::scanDir] current plugins: %d\n", knownPluginList_.getNumTypes());
-
- knownPluginList_.clear(); // clear up previous plugins
-
- std::vector<std::string> dirVec = u::string::split(dirs, ";");
-
- juce::VSTPluginFormat format;
- juce::FileSearchPath searchPath;
- for (const std::string& dir : dirVec)
- searchPath.add(juce::File(dir));
-
- juce::PluginDirectoryScanner scanner(knownPluginList_, format, 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", knownPluginList_.getNumTypes());
- return knownPluginList_.getNumTypes();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int saveList(const std::string& filepath)
-{
- int out = knownPluginList_.createXml()->writeToFile(juce::File(filepath), "");
- if (!out)
- u::log::print("[pluginManager::saveList] unable to save plugin list to %s\n", filepath.c_str());
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int loadList(const std::string& filepath)
-{
- std::unique_ptr<juce::XmlElement> elem(juce::XmlDocument::parse(juce::File(filepath)));
- if (elem == nullptr)
- return 0;
- knownPluginList_.recreateFromXml(*elem);
- return 1;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-std::unique_ptr<Plugin> makePlugin(const std::string& fid, ID id)
-{
- /* Plug-in ID generator is updated anyway, as we store Plugin objects also
- if they are in an invalid state. */
-
- pluginId_.set(id);
-
- const juce::PluginDescription* pd = findPluginDescription_(fid);
- if (pd == nullptr) {
- u::log::print("[pluginManager::makePlugin] no plugin found with fid=%s!\n", fid.c_str());
- missingPlugins_ = true;
- unknownPluginList_.push_back(fid);
- return std::make_unique<Plugin>(pluginId_.get(id), fid); // Invalid plug-in
- }
-
- juce::AudioPluginInstance* pi = pluginFormat_.createInstanceFromDescription(*pd, samplerate_, buffersize_);
- if (pi == nullptr) {
- u::log::print("[pluginManager::makePlugin] unable to create instance with fid=%s!\n", fid.c_str());
- missingPlugins_ = true;
- unknownPluginList_.push_back(fid);
- return std::make_unique<Plugin>(pluginId_.get(id), fid); // Invalid plug-in
- }
- u::log::print("[pluginManager::makePlugin] plugin instance with fid=%s created\n", fid.c_str());
-
- return std::make_unique<Plugin>(pluginId_.get(id), pi, samplerate_, buffersize_);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-std::unique_ptr<Plugin> makePlugin(int index)
-{
- juce::PluginDescription* pd = knownPluginList_.getType(index);
-
- if (pd == nullptr)
- return {};
-
- u::log::print("[pluginManager::makePlugin] plugin found, uid=%s, name=%s...\n",
- pd->createIdentifierString().toRawUTF8(), pd->name.toRawUTF8());
-
- return makePlugin(pd->createIdentifierString().toStdString());
-
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-std::unique_ptr<Plugin> makePlugin(const Plugin& src)
-{
- std::unique_ptr<Plugin> p = makePlugin(src.getUniqueId());
-
- for (int i=0; i<src.getNumParameters(); i++)
- p->setParameter(i, src.getParameter(i));
-
- return p;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-const patch::Plugin serializePlugin(const Plugin& p)
-{
- patch::Plugin pp;
- pp.id = p.id;
- pp.path = p.getUniqueId();
- pp.bypass = p.isBypassed();
-
- for (int i = 0; i < p.getNumParameters(); i++)
- pp.params.push_back(p.getParameter(i));
-
- for (uint32_t param : p.midiInParams)
- pp.midiInParams.push_back(param);
-
- return pp;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-std::unique_ptr<Plugin> deserializePlugin(const patch::Plugin& p)
-{
- std::unique_ptr<Plugin> plugin = makePlugin(p.path, p.id);
- if (!plugin->valid)
- return plugin; // Return invalid version
-
- /* Fill plug-in parameters. */
-
- plugin->setBypass(p.bypass);
- for (unsigned j=0; j<p.params.size(); j++)
- plugin->setParameter(j, p.params.at(j));
-
- /* Fill plug-in MidiIn parameters. Don't fill Channel::midiInParam if
- Plugin::midiInParams are zero: it would wipe out the current default 0x0
- values. */
-
- if (!p.midiInParams.empty()) {
- plugin->midiInParams.clear();
- for (uint32_t midiInParam : p.midiInParams)
- plugin->midiInParams.emplace_back(midiInParam);
- }
-
- return plugin;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int countAvailablePlugins()
-{
- return knownPluginList_.getNumTypes();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-unsigned countUnknownPlugins()
-{
- return unknownPluginList_.size();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-PluginInfo getAvailablePluginInfo(int i)
-{
- juce::PluginDescription* pd = knownPluginList_.getType(i);
- PluginInfo pi;
- pi.uid = pd->fileOrIdentifier.toStdString();
- pi.name = pd->name.toStdString();
- pi.category = pd->category.toStdString();
- pi.manufacturerName = pd->manufacturerName.toStdString();
- pi.format = pd->pluginFormatName.toStdString();
- pi.isInstrument = pd->isInstrument;
- return pi;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool hasMissingPlugins()
-{
- return missingPlugins_;
-};
-
-
-/* -------------------------------------------------------------------------- */
-
-
-std::string getUnknownPluginInfo(int i)
-{
- return unknownPluginList_.at(i);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-bool doesPluginExist(const std::string& fid)
-{
- return pluginFormat_.doesPluginStillExist(*knownPluginList_.getTypeForFile(fid));
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void sortPlugins(SortMethod method)
-{
- switch (method) {
- case SortMethod::NAME:
- knownPluginList_.sort(juce::KnownPluginList::SortMethod::sortAlphabetically, true);
- break;
- case SortMethod::CATEGORY:
- knownPluginList_.sort(juce::KnownPluginList::SortMethod::sortByCategory, true);
- break;
- case SortMethod::MANUFACTURER:
- knownPluginList_.sort(juce::KnownPluginList::SortMethod::sortByManufacturer, true);
- break;
- case SortMethod::FORMAT:
- knownPluginList_.sort(juce::KnownPluginList::SortMethod::sortByFormat, true);
- break;
- }
-}
-}}}; // giada::m::pluginManager::
-
-
-#endif // #ifdef WITH_VST
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-#ifndef G_PLUGIN_MANAGER_H
-#define G_PLUGIN_MANAGER_H
-
-
-#include "deps/juce-config.h"
-#include "plugin.h"
-
-
-namespace giada {
-namespace m
-{
-namespace patch
-{
-struct Plugin;
-}
-namespace pluginManager
-{
-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;
-};
-
-void init(int samplerate, int buffersize);
-
-/* 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<void(float)>& cb);
-
-/* (save|load)List
-(Save|Load) knownPluginList (in|from) an XML file. */
-
-int saveList(const std::string& path);
-int loadList(const std::string& path);
-
-/* countAvailablePlugins
-Returns how many plug-ins are ready and available for usage. */
-
-int countAvailablePlugins();
-
-/* countUnknownPlugins
-Returns how many plug-ins are in a unknown/not-found state. */
-
-unsigned countUnknownPlugins();
-
-std::unique_ptr<Plugin> makePlugin(const std::string& fid, ID id=0);
-std::unique_ptr<Plugin> makePlugin(int index);
-std::unique_ptr<Plugin> makePlugin(const Plugin& other);
-
-/* (de)serializePlugin
-Transforms patch data into a Plugin object and vice versa. */
-
-const patch::Plugin serializePlugin(const Plugin& p);
-std::unique_ptr<Plugin> deserializePlugin(const patch::Plugin& p);
-
-/* getAvailablePluginInfo
-Returns the available plugin information (name, type, ...) given a plug-in
-index. */
-
-PluginInfo getAvailablePluginInfo(int index);
-
-std::string getUnknownPluginInfo(int index);
-
-bool doesPluginExist(const std::string& fid);
-
-bool hasMissingPlugins();
-
-void sortPlugins(SortMethod sortMethod);
-
-}}}; // giada::m::pluginManager::
-
-
-#endif
-
-#endif // #ifdef WITH_VST
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+
+#include <cassert>
+#include <FL/Fl.H>
+#include "utils/log.h"
+#include "utils/time.h"
+#include "core/const.h"
+#include "core/plugins/pluginManager.h"
+#include "plugin.h"
+
+
+using std::string;
+
+
+namespace giada {
+namespace m
+{
+Plugin::Plugin(ID id, const std::string& UID)
+: id (id)
+, valid (false)
+, onEditorResize(nullptr)
+, m_plugin (nullptr)
+, m_UID (UID)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Plugin::Plugin(ID id, std::unique_ptr<juce::AudioPluginInstance> plugin, double samplerate,
+ int buffersize)
+: id (id)
+, valid (true)
+, onEditorResize(nullptr)
+, m_plugin (std::move(plugin))
+, m_bypass (false)
+{
+ /* (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);
+
+ m_plugin->prepareToPlay(samplerate, buffersize);
+
+ u::log::print("[Plugin] plugin initialized and ready. MIDI input params: %lu\n",
+ midiInParams.size());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Plugin::Plugin(const Plugin& o)
+: id (o.id)
+, midiInParams (o.midiInParams)
+, valid (o.valid)
+, onEditorResize(o.onEditorResize)
+, m_plugin (std::move(pluginManager::makePlugin(o)->m_plugin))
+, m_bypass (o.m_bypass.load())
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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<bool>(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<Plugin*>(this));
+ return e;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string Plugin::getUniqueId() const
+{
+ if (!valid)
+ return m_UID;
+ return m_plugin->getPluginDescription().createIdentifierString().toStdString();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int Plugin::getNumParameters() const
+{
+ return valid ? m_plugin->getParameters().size() : 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float Plugin::getParameter(int paramIndex) const
+{
+ return m_plugin->getParameters()[paramIndex]->getValue();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Plugin::setParameter(int paramIndex, float value) const
+{
+ m_plugin->getParameters()[paramIndex]->setValue(value);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string Plugin::getName() const
+{
+ if (!valid)
+ return "** invalid **";
+ return m_plugin->getName().toStdString();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool Plugin::isSuspended() const
+{
+ if (!valid)
+ return false;
+ return m_plugin->isSuspended();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool Plugin::acceptsMidi() const
+{
+ if (!valid)
+ return false;
+ return m_plugin->acceptsMidi();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+PluginState Plugin::getState() const
+{
+ juce::MemoryBlock data;
+ m_plugin->getStateInformation(data);
+ return PluginState(std::move(data));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool Plugin::isBypassed() const { return m_bypass.load(); }
+void Plugin::setBypass(bool b) { m_bypass.store(b); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Plugin::process(juce::AudioBuffer<float>& out, juce::MidiBuffer m)
+{
+ /* If this is not an instrument (i.e. doesn't accept MIDI), copy the
+ incoming buffer data into the temporary one. This way FXes will process
+ existing audio data. Conversely, if the plug-in is an instrument, it
+ generates its own audio data inside a clean m_buffer and we can play more
+ than one plug-in instrument in the same stack, driven by the same set of
+ MIDI events. */
+
+ const bool isInstrument = m_plugin->acceptsMidi();
+
+ if (!isInstrument)
+ m_buffer = out;
+ else
+ m_buffer.clear();
+
+ m_plugin->processBlock(m_buffer, m);
+
+ /* The local buffer is now filled. Let's try to fill the 'out' one as well
+ by taking into account the bus layout - many plug-ins might have mono output
+ and we have a stereo buffer to fill. */
+
+ for (int i=0, j=0; i<out.getNumChannels(); i++) {
+ if (isInstrument)
+ out.addFrom(i, 0, m_buffer, j, 0, m_buffer.getNumSamples());
+ else
+ out.copyFrom(i, 0, m_buffer, j, 0, m_buffer.getNumSamples());
+ if (i < countMainOutChannels() - 1)
+ j++;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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
+{
+ if (!valid)
+ return false;
+ return m_plugin->hasEditor();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string Plugin::getProgramName(int index) const
+{
+ if (!valid)
+ return {};
+ return m_plugin->getProgramName(index).toStdString();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string Plugin::getParameterName(int index) const
+{
+ if (!valid)
+ return {};
+ const int labelSize = 64;
+ return m_plugin->getParameters()[index]->getName(labelSize).toStdString();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string Plugin::getParameterText(int index) const
+{
+ return m_plugin->getParameters()[index]->getCurrentValueAsText().toStdString();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+string Plugin::getParameterLabel(int index) const
+{
+ return m_plugin->getParameters()[index]->getLabel().toStdString();
+}
+
+}} // giada::m::
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+#ifndef G_PLUGIN_H
+#define G_PLUGIN_H
+
+
+#include <vector>
+#include "deps/juce-config.h"
+#include "core/plugins/pluginHost.h"
+#include "core/plugins/pluginState.h"
+#include "core/midiLearnParam.h"
+#include "core/const.h"
+
+
+namespace giada {
+namespace m
+{
+class Plugin : private juce::ComponentListener
+{
+public:
+
+ Plugin(ID id, const std::string& UID);
+ Plugin(ID id, std::unique_ptr<juce::AudioPluginInstance> p, double samplerate, int buffersize);
+ Plugin(const Plugin& o);
+ ~Plugin();
+
+ /* getUniqueId
+ Returns a string-based UID. */
+
+ std::string getUniqueId() const;
+ std::string getName() const;
+ bool hasEditor() const;
+ int getNumParameters() const;
+ float getParameter(int index) const;
+ std::string getParameterName(int index) const;
+ std::string getParameterText(int index) const;
+ std::string getParameterLabel(int index) const;
+ bool isSuspended() const;
+ bool isBypassed() const;
+ int getNumPrograms() const;
+ int getCurrentProgram() const;
+ std::string getProgramName(int index) const;
+ void setParameter(int index, float value) const;
+ void setCurrentProgram(int index) const;
+ bool acceptsMidi() const;
+ PluginState getState() const;
+ juce::AudioProcessorEditor* createEditor() const;
+
+ /* process
+ Process the plug-in with audio and MIDI data. The audio buffer is a reference:
+ it has to be altered by the plug-in itself. Conversely, 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. */
+
+ void process(juce::AudioBuffer<float>& 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<MidiLearnParam> midiInParams;
+
+ /* valid
+ A missing plug-in is loaded anyway, yet marked as 'invalid'. */
+
+ bool valid;
+
+ std::function<void(int w, int h)> 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;
+
+ /* countMainOutChannels
+ Returns the current channel layout for the main output bus. */
+
+ int countMainOutChannels() const;
+
+ std::unique_ptr<juce::AudioPluginInstance> m_plugin;
+ juce::AudioBuffer<float> m_buffer;
+
+ std::atomic<bool> m_bypass;
+
+ /* UID
+ The original UID, used for missing plugins. */
+
+ std::string m_UID;
+};
+}} // giada::m::
+
+#endif
+
+#endif // #ifdef WITH_VST
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+#include <cassert>
+#include "utils/log.h"
+#include "utils/vector.h"
+#include "core/model/model.h"
+#include "core/channels/channel.h"
+#include "core/const.h"
+#include "core/plugins/plugin.h"
+#include "core/plugins/pluginManager.h"
+#include "core/plugins/pluginHost.h"
+
+
+namespace giada {
+namespace m {
+namespace pluginHost
+{
+namespace
+{
+juce::MessageManager* messageManager_;
+juce::AudioBuffer<float> audioBuffer_;
+ID pluginId_;
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void giadaToJuceTempBuf_(const AudioBuffer& outBuf)
+{
+ for (int i=0; i<outBuf.countFrames(); i++)
+ for (int j=0; j<outBuf.countChannels(); j++)
+ audioBuffer_.setSample(j, i, outBuf[i][j]);
+}
+
+
+/* juceToGiadaOutBuf_
+Converts buffer from Juce to Giada. A note for the future: if we overwrite (=)
+(as we do now) it's SEND, if we add (+) it's INSERT. */
+
+void juceToGiadaOutBuf_(AudioBuffer& outBuf)
+{
+ for (int i=0; i<outBuf.countFrames(); i++)
+ for (int j=0; j<outBuf.countChannels(); j++)
+ outBuf[i][j] = audioBuffer_.getSample(j, i);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void processPlugins_(const std::vector<ID>& pluginIds, juce::MidiBuffer& events)
+{
+ model::PluginsLock l(model::plugins);
+
+ for (ID id : pluginIds) {
+ Plugin& p = model::get(model::plugins, id);
+ if (!p.valid || p.isSuspended() || p.isBypassed())
+ continue;
+ p.process(audioBuffer_, events);
+ events.clear();
+ }
+}
+
+
+ID clonePlugin_(ID pluginId)
+{
+ model::PluginsLock l(model::plugins);
+
+ const Plugin& original = model::get(model::plugins, pluginId);
+ std::unique_ptr<Plugin> clone = pluginManager::makePlugin(original);
+ ID newId = clone->id;
+
+ model::plugins.push(std::move(clone));
+
+ return newId;
+}
+} // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+void close()
+{
+ messageManager_->deleteInstance();
+ model::plugins.clear();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void init(int buffersize)
+{
+ messageManager_ = juce::MessageManager::getInstance();
+ audioBuffer_.setSize(G_MAX_IO_CHANS, buffersize);
+ pluginId_ = 0;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void processStack(AudioBuffer& outBuf, const std::vector<ID>& pluginIds,
+ juce::MidiBuffer* events)
+{
+ assert(outBuf.countFrames() == audioBuffer_.getNumSamples());
+
+ /* If events are null: Audio stack processing (master in, master out or
+ sample channels. No need for MIDI events.
+ If events are not null: MIDI stack (MIDI channels). MIDI channels must not
+ process the current buffer: give them an empty and clean one. */
+
+ if (events == nullptr) {
+ giadaToJuceTempBuf_(outBuf);
+ juce::MidiBuffer dummyEvents; // empty
+ processPlugins_(pluginIds, dummyEvents);
+ }
+ else {
+ audioBuffer_.clear();
+ processPlugins_(pluginIds, *events);
+ }
+ juceToGiadaOutBuf_(outBuf);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void addPlugin(std::unique_ptr<Plugin> p, ID channelId)
+{
+ ID pluginId = p->id;
+
+ model::plugins.push(std::move(p));
+
+ model::onSwap(model::channels, channelId, [&](Channel& c)
+ {
+ c.pluginIds.push_back(pluginId);
+ });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void swapPlugin(ID pluginId1, ID pluginId2, ID channelId)
+{
+ model::onSwap(model::channels, channelId, [&](Channel& c)
+ {
+ auto a = u::vector::indexOf(c.pluginIds, pluginId1);
+ auto b = u::vector::indexOf(c.pluginIds, pluginId2);
+
+ std::swap(c.pluginIds.at(a), c.pluginIds.at(b));
+ });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void freePlugin(ID pluginId, ID channelId)
+{
+ model::onSwap(model::channels, channelId, [&](Channel& c)
+ {
+ u::vector::remove(c.pluginIds, pluginId);
+ });
+
+ model::plugins.pop(model::getIndex(model::plugins, pluginId));
+}
+
+
+void freePlugins(const std::vector<ID>& pluginIds)
+{
+ for (ID id : pluginIds)
+ model::plugins.pop(model::getIndex(model::plugins, id));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::vector<ID> clonePlugins(std::vector<ID> pluginIds)
+{
+ std::vector<ID> out;
+ for (ID id : pluginIds)
+ out.push_back(clonePlugin_(id));
+ return out;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void setPluginParameter(ID pluginId, int paramIndex, float value)
+{
+ model::onGet(model::plugins, pluginId, [&](Plugin& p)
+ {
+ p.setParameter(paramIndex, value);
+ });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void setPluginProgram(ID pluginId, int programIndex)
+{
+ model::onGet(model::plugins, pluginId, [&](Plugin& p)
+ {
+ p.setCurrentProgram(programIndex);
+ });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void toggleBypass(ID pluginId)
+{
+ model::onGet(model::plugins, pluginId, [&](Plugin& p)
+ {
+ p.setBypass(!p.isBypassed());
+ });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void runDispatchLoop()
+{
+ messageManager_->runDispatchLoopUntil(10);
+}
+
+}}} // giada::m::pluginHost::
+
+
+#endif // #ifdef WITH_VST
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+
+#ifndef G_PLUGIN_HOST_H
+#define G_PLUGIN_HOST_H
+
+
+#include <functional>
+#include "deps/juce-config.h"
+#include "core/types.h"
+
+
+namespace giada {
+namespace m
+{
+class Plugin;
+class AudioBuffer;
+namespace pluginHost
+{
+using Stack = std::vector<std::shared_ptr<Plugin>>;
+
+void init(int buffersize);
+void close();
+
+/* addPlugin
+Adds a new plugin to channel 'channelId'. */
+
+void addPlugin(std::unique_ptr<Plugin> p, ID channelId);
+
+/* processStack
+Applies the fx list to the buffer. */
+
+void processStack(AudioBuffer& outBuf, const std::vector<ID>& pluginIds,
+ juce::MidiBuffer* events=nullptr);
+
+/* swapPlugin
+Swaps plug-in with ID 1 with plug-in with ID 2 in Channel 'channelId'. */
+
+void swapPlugin(ID pluginId1, ID pluginId2, ID channelId);
+
+/* freePlugin.
+Unloads plugin from channel 'channelId'. */
+
+void freePlugin(ID pluginId, ID channelId);
+
+/* freePlugins
+Unloads multiple plugins. Useful when freeing or deleting a channel. */
+
+void freePlugins(const std::vector<ID>& pluginIds);
+
+/* clonePlugins
+Clones all the plug-ins from 'pluginIds' vector coming from the old channel
+and returns new IDs. */
+
+std::vector<ID> clonePlugins(std::vector<ID> pluginIds);
+
+void setPluginParameter(ID pluginId, int paramIndex, float value);
+
+void setPluginProgram(ID pluginId, int programIndex);
+
+void toggleBypass(ID pluginId);
+
+/* runDispatchLoop
+Wakes up plugins' GUI manager for N milliseconds. */
+
+void runDispatchLoop();
+}}} // giada::m::pluginHost::
+
+
+#endif
+
+#endif // #ifdef WITH_VST
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+
+#include <cassert>
+#include "utils/log.h"
+#include "utils/fs.h"
+#include "utils/string.h"
+#include "core/const.h"
+#include "core/idManager.h"
+#include "core/patch.h"
+#include "core/conf.h"
+#include "core/plugins/plugin.h"
+#include "pluginManager.h"
+
+
+namespace giada {
+namespace m {
+namespace pluginManager
+{
+namespace
+{
+IdManager pluginId_;
+
+int samplerate_;
+int buffersize_;
+
+/* formatManager
+Plugin format manager. */
+
+juce::AudioPluginFormatManager formatManager_;
+
+/* knownPuginList
+List of known (i.e. scanned) plugins. */
+
+juce::KnownPluginList knownPluginList_;
+
+/* unknownPluginList
+List of unrecognized plugins found in a patch. */
+
+std::vector<std::string> unknownPluginList_;
+
+/* missingPlugins
+If some plugins from any stack are missing. */
+
+bool missingPlugins_;
+
+
+std::unique_ptr<Plugin> makeInvalidPlugin_(const std::string& pid, ID id)
+{
+ missingPlugins_ = true;
+ unknownPluginList_.push_back(pid);
+ return std::make_unique<Plugin>(pluginId_.get(id), pid); // Invalid plug-in
+}
+} // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+void init(int samplerate, int buffersize)
+{
+ pluginId_ = IdManager();
+ samplerate_ = samplerate;
+ buffersize_ = buffersize;
+ missingPlugins_ = false;
+
+ formatManager_.addDefaultFormats();
+ unknownPluginList_.clear();
+
+ loadList(u::fs::getHomePath() + G_SLASH + "plugins.xml");
+ sortPlugins(static_cast<pluginManager::SortMethod>(conf::conf.pluginSortMethod));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int scanDirs(const std::string& dirs, const std::function<void(float)>& cb)
+{
+ u::log::print("[pluginManager::scanDir] requested directories: '%s'\n", dirs);
+ u::log::print("[pluginManager::scanDir] current plugins: %d\n", knownPluginList_.getNumTypes());
+
+ knownPluginList_.clear(); // clear up previous plugins
+
+ std::vector<std::string> dirVec = u::string::split(dirs, ";");
+
+ juce::FileSearchPath searchPath;
+ for (const std::string& dir : dirVec)
+ searchPath.add(juce::File(dir));
+
+ for (int i = 0; i < formatManager_.getNumFormats(); i++) {
+
+ juce::PluginDirectoryScanner scanner(knownPluginList_, *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", knownPluginList_.getNumTypes());
+ return knownPluginList_.getNumTypes();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool saveList(const std::string& filepath)
+{
+ bool out = 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 loadList(const std::string& filepath)
+{
+ std::unique_ptr<juce::XmlElement> elem(juce::XmlDocument::parse(juce::File(filepath)));
+ if (elem == nullptr)
+ return false;
+ knownPluginList_.recreateFromXml(*elem);
+ return true;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::unique_ptr<Plugin> makePlugin(const std::string& pid, ID id)
+{
+ /* Plug-in ID generator is updated anyway, as we store Plugin objects also
+ if they are in an invalid state. */
+
+ pluginId_.set(id);
+
+ const std::unique_ptr<juce::PluginDescription> pd = knownPluginList_.getTypeForIdentifierString(pid);
+ if (pd == nullptr) {
+ u::log::print("[pluginManager::makePlugin] no plugin found with pid=%s!\n", pid);
+ return makeInvalidPlugin_(pid, id);
+ }
+
+ juce::String error;
+ std::unique_ptr<juce::AudioPluginInstance> pi = 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<Plugin>(pluginId_.get(id), std::move(pi), samplerate_, buffersize_);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::unique_ptr<Plugin> makePlugin(int index)
+{
+ juce::PluginDescription pd = knownPluginList_.getTypes()[index];
+
+ if (pd.uid == 0) // Invalid
+ return {};
+
+ u::log::print("[pluginManager::makePlugin] plugin found, uid=%s, name=%s...\n",
+ pd.createIdentifierString().toRawUTF8(), pd.name.toRawUTF8());
+
+ return makePlugin(pd.createIdentifierString().toStdString());
+
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::unique_ptr<Plugin> makePlugin(const Plugin& src)
+{
+ std::unique_ptr<Plugin> p = makePlugin(src.getUniqueId());
+
+ for (int i=0; i<src.getNumParameters(); i++)
+ p->setParameter(i, src.getParameter(i));
+
+ return p;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+const patch::Plugin serializePlugin(const Plugin& p)
+{
+ 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<Plugin> deserializePlugin(const patch::Plugin& p, patch::Version version)
+{
+ std::unique_ptr<Plugin> plugin = makePlugin(p.path, p.id);
+ if (!plugin->valid)
+ return plugin; // Return invalid version
+
+ /* Fill plug-in parameters. */
+ plugin->setBypass(p.bypass);
+
+ if (version < patch::Version{0, 17, 0}) // TODO - to be removed in 0.18.0
+ for (unsigned j=0; j<p.params.size(); j++)
+ plugin->setParameter(j, p.params.at(j));
+ else
+ plugin->setState(PluginState(p.state));
+
+ /* Fill plug-in MidiIn parameters. Don't fill Plugin::midiInParam if
+ Patch::midiInParams are zero: it would wipe out the current default 0x0
+ values. */
+
+ if (!p.midiInParams.empty()) {
+ plugin->midiInParams.clear();
+ std::size_t paramIndex = 0;
+ for (uint32_t midiInParam : p.midiInParams)
+ plugin->midiInParams.emplace_back(midiInParam, paramIndex++);
+ }
+
+ return plugin;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int countAvailablePlugins()
+{
+ return knownPluginList_.getNumTypes();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+int countUnknownPlugins()
+{
+ return unknownPluginList_.size();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+PluginInfo getAvailablePluginInfo(int i)
+{
+ juce::PluginDescription pd = 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;
+ return pi;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool hasMissingPlugins()
+{
+ return missingPlugins_;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+std::string getUnknownPluginInfo(int i)
+{
+ return unknownPluginList_.at(i);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool doesPluginExist(const std::string& pid)
+{
+ return formatManager_.doesPluginStillExist(*knownPluginList_.getTypeForFile(pid));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void sortPlugins(SortMethod method)
+{
+ switch (method) {
+ case SortMethod::NAME:
+ knownPluginList_.sort(juce::KnownPluginList::SortMethod::sortAlphabetically, true);
+ break;
+ case SortMethod::CATEGORY:
+ knownPluginList_.sort(juce::KnownPluginList::SortMethod::sortByCategory, true);
+ break;
+ case SortMethod::MANUFACTURER:
+ knownPluginList_.sort(juce::KnownPluginList::SortMethod::sortByManufacturer, true);
+ break;
+ case SortMethod::FORMAT:
+ knownPluginList_.sort(juce::KnownPluginList::SortMethod::sortByFormat, true);
+ break;
+ }
+}
+}}} // giada::m::pluginManager::
+
+
+#endif // #ifdef WITH_VST
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+#ifndef G_PLUGIN_MANAGER_H
+#define G_PLUGIN_MANAGER_H
+
+
+#include "deps/juce-config.h"
+#include "plugin.h"
+
+
+namespace giada {
+namespace m
+{
+namespace patch
+{
+struct Plugin;
+struct Version;
+}
+namespace pluginManager
+{
+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;
+};
+
+void init(int samplerate, int buffersize);
+
+/* 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<void(float)>& cb);
+
+/* (save|load)List
+(Save|Load) knownPluginList (in|from) an XML file. */
+
+bool saveList(const std::string& path);
+bool loadList(const std::string& path);
+
+/* countAvailablePlugins
+Returns how many plug-ins are ready and available for usage. */
+
+int countAvailablePlugins();
+
+/* countUnknownPlugins
+Returns how many plug-ins are in a unknown/not-found state. */
+
+int countUnknownPlugins();
+
+std::unique_ptr<Plugin> makePlugin(const std::string& pid, ID id=0);
+std::unique_ptr<Plugin> makePlugin(int index);
+std::unique_ptr<Plugin> makePlugin(const Plugin& other);
+
+/* (de)serializePlugin
+Transforms patch data into a Plugin object and vice versa. */
+
+const patch::Plugin serializePlugin(const Plugin& p);
+std::unique_ptr<Plugin> deserializePlugin(const patch::Plugin& p, patch::Version version);
+
+/* getAvailablePluginInfo
+Returns the available plugin information (name, type, ...) given a plug-in
+index. */
+
+PluginInfo getAvailablePluginInfo(int index);
+
+std::string getUnknownPluginInfo(int index);
+
+bool doesPluginExist(const std::string& pid);
+
+bool hasMissingPlugins();
+
+void sortPlugins(SortMethod sortMethod);
+
+}}} // giada::m::pluginManager::
+
+
+#endif
+
+#endif // #ifdef WITH_VST
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+#ifdef WITH_VST
+
+#include <cassert>
+#include "pluginState.h"
+
+
+namespace giada {
+namespace m
+{
+PluginState::PluginState(juce::MemoryBlock&& data)
+: m_data(std::move(data))
+{
+}
+
+/* -------------------------------------------------------------------------- */
+
+
+PluginState::PluginState(const std::string& base64)
+{
+ bool res = m_data.fromBase64Encoding(base64);
+ assert(res);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+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();
+}
+}}
+
+#endif // #ifdef WITH_VST
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+
+#ifndef G_PLUGIN_STATE_H
+#define G_PLUGIN_STATE_H
+
+
+#include <string>
+#include "deps/juce-config.h"
+
+
+namespace giada {
+namespace m
+{
+class PluginState
+{
+public:
+
+ 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;
+};
+}}
+
+#endif
+
+#endif // #ifdef WITH_VST
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cassert>
+#include "core/clock.h"
+#include "quantizer.h"
+
+
+namespace giada {
+namespace m
+{
+void Quantizer::trigger(int id)
+{
+ assert(id >= 0);
+ assert(id < (int) m_callbacks.size());
+
+ m_performId = id;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Quantizer::schedule(int id, std::function<void(Frame delta)> f)
+{
+ assert(id >= 0);
+ assert(id < (int) m_callbacks.size());
+
+ m_callbacks[id] = f;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Quantizer::advance(Range<Frame> block, Frame quantizerStep)
+{
+ /* Nothing to do if there's no action to perform. */
+
+ if (m_performId == -1)
+ return;
+
+ assert(m_callbacks[m_performId] != nullptr);
+
+ 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[m_performId](local);
+ m_performId = -1;
+ return;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Quantizer::clear()
+{
+ m_performId = -1;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool Quantizer::isTriggered() const
+{
+ return m_performId != -1;
+}
+}} // giada::m::
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_QUANTIZER_H
+#define G_QUANTIZER_H
+
+
+#include <array>
+#include <functional>
+#include "core/const.h"
+#include "core/range.h"
+#include "core/types.h"
+
+
+namespace giada {
+namespace 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<void(Frame)>);
+
+ /* 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<Frame> block, Frame quantizerStep);
+
+ /* clear
+ Disables quantized operations in progress, if any. */
+
+ void clear();
+
+ /* isTriggered
+ True if a quantizer function has been triggered(). */
+
+ bool isTriggered() const;
+
+private:
+
+ std::array<std::function<void(Frame)>, G_MAX_QUANTIZER_SIZE> m_callbacks;
+ int m_performId = -1;
+};
+}} // giada::m::
+
+
+#endif
/* Queue
Single producer, single consumer lock-free queue. */
-template<typename T, size_t size>
+template<typename T, std::size_t size>
class Queue
{
public:
bool pop(T& item)
{
- size_t curr = m_head.load();
+ std::size_t curr = m_head.load();
if (curr == m_tail.load()) // Queue empty, nothing to do
return false;
bool push(const T& item)
{
- size_t curr = m_tail.load();
- size_t next = increment(curr);
+ 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;
private:
- size_t increment(size_t i) const
+ std::size_t increment(std::size_t i) const
{
return (i + 1) % size;
}
std::array<T, size> m_data;
- std::atomic<size_t> m_head;
- std::atomic<size_t> m_tail;
+ std::atomic<std::size_t> m_head;
+ std::atomic<std::size_t> m_tail;
};
}} // giada::m::
template<typename T>
class Range
{
-private:
-
- T m_a;
- T m_b;
-
public:
Range() : m_a(0), m_b(0) {}
T getBegin() const { return m_a; }
T getEnd() const { return m_b; }
T getLength() const { return m_b - m_a; }
+
+private:
+
+ T m_a;
+ T m_b;
};
} // giada::
namespace giada {
namespace m
{
+/* RCUList
+Single producer, multiple consumer (i.e. one writer, many readers) RCU-based
+list. */
+
template<typename T>
class RCUList
{
public:
/* Lock
- Scoped lock structure. */
+ Scoped lock structure. Not copyable, not moveable, not copy-constructible.
+ Same as std::scoped_lock:
+ https://en.cppreference.com/w/cpp/thread/scoped_lock .*/
struct Lock
{
- Lock(RCUList<T>& r) : rcu(r)
- {
- rcu.lock();
- }
-
- ~Lock()
- {
- rcu.unlock();
- }
+ Lock(RCUList<T>& r) : rcu(r) { rcu.lock(); }
+ Lock(const Lock&) = delete;
+ Lock& operator=(const Lock&) = delete;
+ ~Lock() { rcu.unlock(); }
RCUList<T>& rcu;
};
void unlock()
{
m_readers[t_grace]--;
+ assert(m_readers[t_grace] >= 0 && "Negative reader");
}
/* get
Returns a reference to the data held by node 'i'. */
- // TODO - this will return a const ref with the non-virtual Channel
- // refactoring.
- T* get(size_t i=0) const
+ T* get(std::size_t i=0) const
{
assert(i < size() && "Index overflow");
assert(m_readers[t_grace].load() > 0 && "Forgot lock before reading");
/* Subscript operator []
Same as above for the [] syntax. */
- // TODO - this will return a const ref with the non-virtual Channel
- // refactoring.
- T* operator[] (size_t i) const
+ T* operator[] (std::size_t i) const
{
return get(i);
}
/* back
Return data held by the last node. */
- // TODO - this will return a const ref with the non-virtual Channel
- // refactoring.
T* back() const
{
}
/* clone
- Returns a new copy of the data held by node 'i'. The template machinery
- is required for when you declare a RCUList<Base> and later on want to clone
- a derived object. Usage:
-
- RCUList<Base> list;
- ...
- std::unique_ptr<Derived> d = list.clone<Derived>(i);
- */
+ Returns a new copy of the data held by node 'i'. */
- template<typename C=T>
- std::unique_ptr<C> clone(size_t i=0) const
+ std::unique_ptr<T> clone(std::size_t i=0) const
{
- return std::make_unique<C>(*static_cast<C*>(getNode(i)->data.get()));
+ /* Make sure no one is writing (swapping, popping, pushing). */
+ assert(m_writing.load() == false);
+ return std::make_unique<T>(*getNode(i)->data.get());
}
/* swap
multiple calls to swap() made by the same thread: the caller is blocked by
the spinlock below: no progress is made until m_readers[oldgrace] > 0. */
- void swap(std::unique_ptr<T> data, size_t i=0)
+ void swap(std::unique_ptr<T> data, std::size_t i=0)
{
/* Never start two overlapping writing sessions. */
calls to pop() made by the same thread: the caller is blocked by the
spinlock below: no progress is made while m_readers[oldgrace] > 0. */
- void pop(size_t i)
+ void pop(std::size_t i)
{
/* Never start two overlapping writing sessions. */
/* size
Returns the number of nodes in the list. */
- size_t size() const
+ std::size_t size() const
{
return m_size.load();
}
private:
- Node* getNode(size_t i) const
+ Node* getNode(std::size_t i) const
{
- size_t p = 0;
+ std::size_t p = 0;
Node* curr = m_head.load();
while (curr != nullptr && p < i) {
std::array<std::atomic<int>, 2> m_readers;
std::atomic<std::int8_t> m_grace;
- std::atomic<size_t> m_size;
+ std::atomic<std::size_t> m_size;
std::atomic<bool> m_writing;
/* m_head
#include "gui/dispatcher.h"
-#include "core/channels/channel.h"
#include "core/model/model.h"
#include "core/types.h"
#include "core/clock.h"
#include "core/kernelAudio.h"
#include "core/conf.h"
#include "core/mixer.h"
+#include "core/sequencer.h"
#include "core/mixerHandler.h"
#include "core/midiDispatcher.h"
#include "core/recorder.h"
if (!kernelAudio::isReady())
return false;
clock::setStatus(ClockStatus::RUNNING);
- m::mh::startSequencer();
+ sequencer::start();
+ m::conf::conf.recTriggerMode = RecTriggerMode::NORMAL;
return true;
}
bool startInputRec_()
{
- if (!kernelAudio::isReady() || !mh::hasRecordableSampleChannels())
+ if (!kernelAudio::isReady() || !mh::hasInputRecordableChannels())
return false;
mixer::startInputRec();
- mh::startSequencer();
+ sequencer::start();
+ m::conf::conf.recTriggerMode = RecTriggerMode::NORMAL;
return true;
}
} // {anonymous}
if (clock::getStatus() == ClockStatus::WAITING) {
clock::setStatus(ClockStatus::STOPPED);
- m::midiDispatcher::setSignalCallback(nullptr);
+ midiDispatcher::setSignalCallback(nullptr);
v::dispatcher::setSignalCallback(nullptr);
return;
}
/* 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. */
+ conf::treatRecsAsLoops is enabled or not. Same thing for MIDI channels. */
- for (ID id : channels)
- m::model::onGet(m::model::channels, id, [](Channel& c)
+ for (ID id : channels) {
+ model::onGet(model::channels, id, [](Channel& c)
{
- c.startReadingActions(/*treatRecsAsLoops=*/false, /*recsStopOnChanHalt=*/false);
+ c.state->readActions.store(true);
+ if (c.getType() == ChannelType::MIDI)
+ c.state->playStatus.store(ChannelStatus::PLAY);
});
+ }
}
bool startInputRec(RecTriggerMode mode)
{
if (mode == RecTriggerMode::NORMAL) {
+G_DEBUG("Start input rec, NORMAL mode");
if (!startInputRec_())
return false;
setRecordingInput_(true);
return true;
}
- else { // RecTriggerMode::SIGNAL
- if (!mh::hasRecordableSampleChannels())
+ else {
+G_DEBUG("Start input rec, SIGNAL mode");
+ if (!mh::hasInputRecordableChannels())
return false;
clock::setStatus(ClockStatus::WAITING);
clock::rewind();
#include <cassert>
#include "utils/log.h"
#include "core/model/model.h"
-#include "core/channels/channel.h"
#include "core/action.h"
#include "core/idManager.h"
#include "core/recorder.h"
Action* findAction_(ActionMap& src, ID id)
{
- for (auto& kv : src)
- for (Action& a : kv.second)
+ for (auto& [frame, actions] : src)
+ for (Action& a : actions)
if (a.id == id)
return &a;
assert(false);
{
model::onSwap(model::actions, [&](model::Actions& a)
{
- for (auto& kv : a.map) {
- std::vector<Action>& as = kv.second;
- as.erase(std::remove_if(as.begin(), as.end(), f), as.end());
- }
+ for (auto& [frame, actions] : a.map)
+ actions.erase(std::remove_if(actions.begin(), actions.end(), f), actions.end());
optimize_(a.map);
updateMapPointers(a.map);
});
}
+
+/* -------------------------------------------------------------------------- */
+
+
+bool exists_(ID channelId, Frame frame, const MidiEvent& event, const ActionMap& target)
+{
+ 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 exists_(ID channelId, Frame frame, const MidiEvent& event)
+{
+ model::ActionsLock lock(model::actions);
+ return exists_(channelId, frame, event, model::actions.get()->map);
+}
} // {anonymous}
{
std::unique_ptr<model::Actions> ma = model::actions.clone();
- /* Remove all existing actions: let's start from scratch. */
+ /* Remove all existing actions: let's start from scratch.
+ TODO - so why cloning it?! */
ma->map.clear();
{
model::ActionsLock lock(model::actions);
- for (const auto& kv : model::actions.get()->map) {
- Frame frame = f(kv.first);
- for (const Action& a : kv.second) {
+ for (const auto& [oldFrame, actions] : model::actions.get()->map) {
+ Frame newFrame = f(oldFrame);
+ for (const Action& a : actions) {
Action copy = a;
- copy.frame = frame;
- ma->map[frame].push_back(copy);
+ copy.frame = newFrame;
+ ma->map[newFrame].push_back(copy);
}
- u::log::print("[recorder::updateKeyFrames] %d -> %d\n", kv.first, frame);
+G_DEBUG(oldFrame << " -> " << newFrame);
}
}
{
model::ActionsLock lock(model::actions);
- for (const auto& kv : model::actions.get()->map)
- for (const Action& a : kv.second)
+ for (const auto& [frame, actions] : model::actions.get()->map)
+ for (const Action& a : actions)
if (a.channelId == channelId && (type == 0 || type == a.event.getStatus()))
return true;
return false;
Action 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
/* -------------------------------------------------------------------------- */
-void rec(std::vector<Action>& as)
+void rec(std::vector<Action>& actions)
{
- if (as.size() == 0)
+ if (actions.size() == 0)
return;
- /* Generate new action ID and fix next and prev IDs. */
-
- for (Action& a : as) {
- int id = a.id;
- a.id = actionId_.get();
- for (Action& aa : as) {
- if (aa.prevId == id) aa.prevId = a.id;
- if (aa.nextId == id) aa.nextId = a.id;
- }
- }
-
model::onSwap(model::actions, [&](model::Actions& mas)
{
- for (const Action& a : as)
- mas.map[a.frame].push_back(a);
+ for (const Action& a : actions)
+ if (!exists_(a.channelId, a.frame, a.event, mas.map))
+ mas.map[a.frame].push_back(a);
updateMapPointers(mas.map);
});
}
{
model::ActionsLock lock(model::actions);
- for (auto& kv : model::actions.get()->map)
- for (const Action& action : kv.second)
+ for (auto& [_, actions] : model::actions.get()->map)
+ for (const Action& action : actions)
f(action);
}
-}}}; // giada::m::recorder::
+
+
+/* -------------------------------------------------------------------------- */
+
+
+ID getNewActionId()
+{
+ return actionId_.get();
+}
+}}} // giada::m::recorder::
constructor. */
void updateMapPointers(ActionMap& src);
-}}}; // giada::m::recorder::
+
+/* getNewActionId
+Returns a new action ID, internally generated. */
+
+ID getNewActionId();
+
+}}} // giada::m::recorder::
#endif
* -------------------------------------------------------------------------- */
+#include <unordered_map>
#include <algorithm>
#include <cmath>
#include <cassert>
const Action* getActionPtrById_(int id, const recorder::ActionMap& source)
{
- for (auto& kv : source)
- for (const Action& action : kv.second)
+ for (const auto& [_, actions] : source)
+ for (const Action& action : actions)
if (action.id == id)
return &action;
return nullptr;
Without this trick (i.e. if it loops from vector.begin() each time) the
algorithm would end up matching wrong partners. */
-void consolidate_(const Action& a1, size_t i)
+void consolidate_(const Action& a1, std::size_t i)
{
for (auto it = recs_.begin() + i; it != recs_.end(); ++it) {
quantizer set to 44100. That would mean two recs completely useless. So we
compute a reject value ('scarto'): if it's lower than 6 frames the new frame
is collapsed with a quantized frame. FIXME - maybe 6 frames are too low. */
- Frame frame = (old / newval) * oldval;
+ Frame frame = static_cast<Frame>((old / newval) * oldval);
if (frame != 0) {
Frame delta = oldquanto % frame;
if (delta > 0 && delta <= 6)
{
bool cloned = false;
std::vector<Action> actions;
+ std::unordered_map<ID, ID> map; // Action ID mapper, old -> new
recorder::forEachAction([&](const Action& a)
{
if (a.channelId != channelId)
return;
+
+ ID newActionId = recorder::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);
+ }
+
recorder::rec(actions);
return cloned;
/* -------------------------------------------------------------------------- */
-void liveRec(ID channelId, MidiEvent e)
+void liveRec(ID channelId, MidiEvent e, Frame globalFrame)
{
assert(e.isNoteOnOff()); // Can't record any other kind of events for now
+ /* TODO - this might allocate on the MIDI thread */
if (recs_.size() >= recs_.capacity())
recs_.reserve(recs_.size() + MAX_LIVE_RECS_CHUNK);
- recs_.push_back(recorder::makeAction(-1, channelId, clock::getCurrentFrame(), e));
+ recs_.push_back(recorder::makeAction(recorder::getNewActionId(), channelId, globalFrame, e));
}
void clearAllActions()
{
- for (size_t i = 0; i < model::channels.size(); i++)
- model::onSwap(model::channels, model::getId(model::channels, i), [](Channel& c) { c.hasActions = false; });
+ /* TODO - disgusting */
+ for (std::size_t i = 0; i < model::channels.size(); i++) {
+ model::onSwap(model::channels, model::getId(model::channels, i), [](Channel& c)
+ {
+ c.state->hasActions = false;
+ });
+ }
recorder::clearAll();
}
return out;
}
-}}}; // giada::m::recorderHandler::
+}}} // giada::m::recorderHandler::
/* liveRec
Records a user-generated action. NOTE_ON or NOTE_OFF only for now. */
-void liveRec(ID channelId, MidiEvent e);
+void liveRec(ID channelId, MidiEvent e, Frame global);
/* consolidate
Records all live actions. Returns a set of channels IDs that have been
recorder::ActionMap deserializeActions(const std::vector<patch::Action>& as);
std::vector<patch::Action> serializeActions(const recorder::ActionMap& as);
-}}}; // giada::m::recorderHandler::
+}}} // giada::m::recorderHandler::
#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_RING_BUFFER_H
+#define G_RING_BUFFER_H
+
+
+#include <array>
+
+
+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 <typename T, std::size_t S>
+class RingBuffer
+{
+public:
+ using iterator = typename std::array<T, S>::iterator;
+ using const_iterator = typename std::array<T, S>::const_iterator;
+
+ iterator begin() { return m_data.begin(); }
+ iterator end() { return m_data.begin() + m_end; }
+ const_iterator begin() const { return m_data.begin(); }
+ const_iterator end() const { return m_data.begin() + m_end; }
+ 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
+ }
+
+ constexpr std::size_t size() const noexcept
+ {
+ return m_data.size();
+ }
+
+private:
+
+ std::array<T, S> m_data;
+ std::size_t m_index = 0;
+ std::size_t m_end = 0;
+};
+} // giada::
+
+
+#endif
+
+
+
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "core/model/model.h"
+#include "core/const.h"
+#include "core/mixer.h"
+#include "core/quantizer.h"
+#include "core/clock.h"
+#include "core/conf.h"
+#include "core/recManager.h"
+#include "core/kernelAudio.h"
+#include "sequencer.h"
+
+
+namespace giada {
+namespace m {
+namespace sequencer
+{
+namespace
+{
+constexpr int Q_ACTION_REWIND = 0;
+
+
+/* -------------------------------------------------------------------------- */
+
+
+struct Metronome
+{
+ static constexpr Frame CLICK_SIZE = 38;
+
+ float beat[CLICK_SIZE] = {
+ 0.059033f, 0.117240f, 0.173807f, 0.227943f, 0.278890f, 0.325936f,
+ 0.368423f, 0.405755f, 0.437413f, 0.462951f, 0.482013f, 0.494333f,
+ 0.499738f, 0.498153f, 0.489598f, 0.474195f, 0.452159f, 0.423798f,
+ 0.389509f, 0.349771f, 0.289883f, 0.230617f, 0.173194f, 0.118739f,
+ 0.068260f, 0.022631f, -0.017423f, -0.051339f, -0.078721f, -0.099345f,
+ -0.113163f, -0.120295f, -0.121028f, -0.115804f, -0.105209f, -0.089954f,
+ -0.070862f, -0.048844f
+ };
+
+ float bar[CLICK_SIZE] = {
+ 0.175860f, 0.341914f, 0.488904f, 0.608633f, 0.694426f, 0.741500f,
+ 0.747229f, 0.711293f, 0.635697f, 0.524656f, 0.384362f, 0.222636f,
+ 0.048496f, -0.128348f, -0.298035f, -0.451105f, -0.579021f, -0.674653f,
+ -0.732667f, -0.749830f, -0.688924f, -0.594091f, -0.474481f, -0.340160f,
+ -0.201360f, -0.067752f, 0.052194f, 0.151746f, 0.226280f, 0.273493f,
+ 0.293425f, 0.288307f, 0.262252f, 0.220811f, 0.170435f, 0.117887f,
+ 0.069639f, 0.031320f
+ };
+
+ Frame tracker = 0;
+ bool running = false;
+ bool playBar = false;
+ bool playBeat = false;
+
+ void render(AudioBuffer& outBuf, bool& process, float* data, Frame f)
+ {
+ process = true;
+ for (int i=0; i<outBuf.countChannels(); i++)
+ outBuf[f][i] += data[tracker];
+ if (++tracker > Metronome::CLICK_SIZE) {
+ process = false;
+ tracker = 0;
+ }
+ }
+} metronome_;
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void renderMetronome_(AudioBuffer& outBuf, Frame f)
+{
+ if (!metronome_.running)
+ return;
+
+ if (clock::isOnBar() || metronome_.playBar)
+ metronome_.render(outBuf, metronome_.playBar, metronome_.bar, f);
+ else
+ if (clock::isOnBeat() || metronome_.playBeat)
+ metronome_.render(outBuf, metronome_.playBeat, metronome_.beat, f);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void rewindQ_(Frame delta)
+{
+ clock::rewind();
+ mixer::pumpEvent({ mixer::EventType::SEQUENCER_REWIND, delta, {} });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void start_()
+{
+#ifdef WITH_AUDIO_JACK
+ if (kernelAudio::getAPI() == G_SYS_API_JACK)
+ kernelAudio::jackStart();
+ else
+#endif
+ start();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void stop_()
+{
+#ifdef WITH_AUDIO_JACK
+ if (kernelAudio::getAPI() == G_SYS_API_JACK)
+ kernelAudio::jackStop();
+ else
+#endif
+ stop();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void rewind_()
+{
+#ifdef WITH_AUDIO_JACK
+ if (kernelAudio::getAPI() == G_SYS_API_JACK)
+ kernelAudio::jackSetPosition(0);
+ else
+#endif
+ rewind();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Quantizer quantizer_;
+} // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+void init()
+{
+ quantizer_.schedule(Q_ACTION_REWIND, rewindQ_);
+ clock::rewind();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void run(Frame bufferSize)
+{
+ Frame start = clock::getCurrentFrame();
+ Frame end = start + bufferSize;
+ Frame total = clock::getFramesInLoop();
+ Frame bar = clock::getFramesInBar();
+
+ for (Frame i = start, local = 0; i < end; i++, local++) {
+
+ Frame global = i % total; // wraps around 'total'
+
+ if (global == 0)
+ mixer::pumpEvent({ mixer::EventType::SEQUENCER_FIRST_BEAT, local, { 0, 0, global, {} } });
+ else
+ if (global % bar == 0)
+ mixer::pumpEvent({ mixer::EventType::SEQUENCER_BAR, local, { 0, 0, global, {} } });
+
+ const std::vector<Action>* as = recorder::getActionsOnFrame(global);
+ if (as != nullptr)
+ for (const Action& a : *as)
+ mixer::pumpEvent({ mixer::EventType::ACTION, local, a });
+ }
+
+ quantizer_.advance(Range<Frame>(start, end), clock::getQuantizerStep());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void parse(const mixer::EventBuffer& events)
+{
+ for (const mixer::Event& e : events) {
+ if (e.type == mixer::EventType::SEQUENCER_START) {
+ start_(); break;
+ }
+ if (e.type == mixer::EventType::SEQUENCER_STOP) {
+ stop_(); break;
+ }
+ if (e.type == mixer::EventType::SEQUENCER_REWIND_REQ) {
+ rewind_(); break;
+ }
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void advance(AudioBuffer& outBuf)
+{
+ for (Frame i = 0; i < outBuf.countFrames(); i++) {
+ clock::sendMIDIsync();
+ clock::incrCurrentFrame();
+ renderMetronome_(outBuf, i);
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void start()
+{
+ switch (clock::getStatus()) {
+ case ClockStatus::STOPPED:
+ clock::setStatus(ClockStatus::RUNNING);
+ break;
+ case ClockStatus::WAITING:
+ clock::setStatus(ClockStatus::RUNNING);
+ recManager::stopActionRec();
+ break;
+ default:
+ break;
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void stop()
+{
+ clock::setStatus(ClockStatus::STOPPED);
+
+ /* If recordings (both input and action) are active deactivate them, but
+ store the takes. RecManager takes care of it. */
+
+ if (recManager::isRecordingAction())
+ recManager::stopActionRec();
+ else
+ if (recManager::isRecordingInput())
+ recManager::stopInputRec();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void rewind()
+{
+ if (clock::canQuantize())
+ quantizer_.trigger(Q_ACTION_REWIND);
+ else
+ rewindQ_(/*delta=*/0);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool isMetronomeOn() { return metronome_.running; }
+void toggleMetronome() { metronome_.running = !metronome_.running; }
+void setMetronome(bool v) { metronome_.running = v; }
+}}} // giada::m::sequencer::
+
+
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_SEQUENCER_H
+#define G_SEQUENCER_H
+
+
+#include "core/mixer.h"
+
+
+namespace giada {
+namespace m
+{
+class AudioBuffer;
+namespace sequencer
+{
+void init();
+
+/* parse
+Parses sequencer events that might occur in a block and advances the internal
+quantizer. */
+
+void run(Frame bufferSize);
+void parse(const mixer::EventBuffer& events);
+void advance(AudioBuffer& outBuf);
+
+void start();
+void stop();
+void rewind();
+
+bool isMetronomeOn();
+void toggleMetronome();
+void setMetronome(bool v);
+}}} // giada::m::sequencer::
+
+
+#endif
using Pixel = int;
using Frame = int;
-enum class ClockStatus { STOPPED, WAITING, RUNNING };
+enum class Thread { MAIN, MIDI, AUDIO };
+
+/* Windows fix */
+#ifdef _WIN32
+#undef VOID
+#endif
+enum class ClockStatus { STOPPED, WAITING, RUNNING, ON_BEAT, ON_BAR, ON_FIRST_BEAT, VOID };
enum class ChannelType : int { SAMPLE = 1, MIDI, MASTER, PREVIEW };
ENDING = 1, WAIT, PLAY, OFF, EMPTY, MISSING, WRONG
};
-enum class ChannelMode : int
+enum class SamplePlayerMode : int
{
LOOP_BASIC = 1, LOOP_ONCE, LOOP_REPEAT, LOOP_ONCE_BAR,
SINGLE_BASIC, SINGLE_PRESS, SINGLE_RETRIG, SINGLE_ENDLESS
enum class RecTriggerMode : int { NORMAL = 0, SIGNAL };
-enum class PreviewMode : int { NONE = 0, NORMAL, LOOP };
enum class EventType : int { AUTO = 0, MANUAL };
-};
+}
#endif
/* -------------------------------------------------------------------------- */
-void Wave::setPath(const std::string& p, int id)
+void Wave::setPath(const std::string& p, int wid)
{
- if (id == -1)
+ if (wid == -1)
m_path = p;
else
- m_path = u::fs::stripExt(p) + "-" + std::to_string(id) + "." + u::fs::getExt(p);
+ m_path = u::fs::stripExt(p) + "-" + std::to_string(wid) + u::fs::getExt(p);
}
/* -------------------------------------------------------------------------- */
-void Wave::copyData(const float* data, int frames, int offset)
+void Wave::copyData(const float* data, int frames, int channels, int offset)
{
- buffer.copyData(data, frames, offset);
+ buffer.copyData(data, frames, channels, offset);
}
+void Wave::copyData(const AudioBuffer& b) { buffer.copyData(b); }
+void Wave::addData(const AudioBuffer& b) { buffer.addData(b); }
+
+
/* -------------------------------------------------------------------------- */
{
buffer.moveData(b);
}
-
-}}; // giada::m::
+}} // giada::m::
/* copyData
Copies 'frames' frames from the new 'data' into m_data, starting from frame
- 'offset'. It takes for granted that the new data contains the same number of
- channels than m_channels. */
+ 'offset'. */
- void copyData(const float* data, int frames, int offset=0);
+ void copyData(const float* data, int frames, int channels, int offset=0);
+ void copyData(const AudioBuffer& b);
+
+ /* addData
+ Merges audio data from buffer 'b' onto this one. */
+
+ void addData(const AudioBuffer& b);
void alloc(int size, int channels, int rate, int bits, const std::string& path);
bool m_edited; // edited via editor
std::string m_path; // E.g. /path/to/my/sample.wav
};
-}}; // giada::m::
+}} // giada::m::
#endif
}
return peak;
}
-}; // {anonymous}
+} // {anonymous}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-void normalizeHard(ID waveId, int a, int b)
+constexpr int SMOOTH_SIZE = 32;
+
+
+void normalize(ID waveId, int a, int b)
{
model::onSwap(m::model::waves, waveId, [&](Wave& w)
{
float peak = getPeak_(w, a, b);
- if (peak == 0.0f || peak > 1.0f) // as in ::normalizeSoft
+ if (peak == 0.0f || peak > 1.0f)
return;
for (int i=a; i<b; i++) {
/* -------------------------------------------------------------------------- */
-void fade(ID waveId, int a, int b, int type)
+void fade(ID waveId, int a, int b, Fade type)
{
u::log::print("[wfx::fade] fade from %d to %d (range = %d)\n", a, b, b-a);
model::onSwap(m::model::waves, waveId, [&](Wave& w)
{
- if (type == FADE_IN)
+ if (type == Fade::IN)
for (int i=a; i<=b; i++, m+=d)
fadeFrame_(w, i, m);
else
return;
}
- fade(waveId, a, a+SMOOTH_SIZE, FADE_IN);
- fade(waveId, b-SMOOTH_SIZE, b, FADE_OUT);
+ fade(waveId, a, a+SMOOTH_SIZE, Fade::IN);
+ fade(waveId, b-SMOOTH_SIZE, b, Fade::OUT);
}
});
}
-}}}; // giada::m::wfx::
+}}} // giada::m::wfx::
#define G_WAVE_FX_H
+#include "core/types.h"
+
+
namespace giada {
namespace m
{
class Wave;
-
namespace wfx
{
-static const int FADE_IN = 0;
-static const int FADE_OUT = 1;
-static const int SMOOTH_SIZE = 32;
+/* 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. It works on a free Wave object,
int monoToStereo(Wave& w);
-/* normalizeHard
+/* normalize
Normalizes the wave in range a-b by altering values in memory. */
-void normalizeHard(ID waveId, int a, int b);
+void normalize(ID waveId, int a, int b);
void silence(ID waveId, int a, int b);
void cut(ID waveId, int a, int b);
void paste(const Wave& src, ID waveId, int a);
/* fade
-Fades in or fades out selection. Fade In = type 0, Fade Out = type 1 */
+Fades in or fades out selection. Can be Fade::IN or Fade::OUT. */
-void fade(ID waveId, int a, int b, int type);
+void fade(ID waveId, int a, int b, Fade type);
/* smooth
Smooth edges of selection. */
void reverse(ID waveId, int a, int b);
void shift(ID waveId, int offset);
-}}}; // giada::m::wfx::
+}}} // giada::m::wfx::
#endif
return 64;
return 0;
}
-}; // {anonymous}
+} // {anonymous}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-Result createFromFile(const std::string& path, ID id)
+Result 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.c_str());
+ u::log::print("[waveManager::create] malformed path (was '%s')\n", path);
return { G_RES_ERR_NO_DATA };
}
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.c_str(), sf_strerror(fileIn));
+ u::log::print("[waveManager::create] unable to read %s. %s\n", path, sf_strerror(fileIn));
return { G_RES_ERR_IO };
}
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->getSize());
std::unique_ptr<Wave> wave = std::make_unique<Wave>(waveId_.get());
wave->alloc(frames, channels, src.getRate(), src.getBits(), src.getPath());
- wave->copyData(src.getFrame(a), frames);
+ wave->copyData(src.getFrame(a), frames, channels);
wave->setLogical(true);
u::log::print("[waveManager::createFromWave] new Wave created, %d frames\n", frames);
/* -------------------------------------------------------------------------- */
-std::unique_ptr<Wave> deserializeWave(const patch::Wave& w)
+std::unique_ptr<Wave> deserializeWave(const patch::Wave& w, int samplerate, int quality)
{
- return createFromFile(w.path, w.id).wave;
+ return createFromFile(w.path, w.id, samplerate, quality).wave;
}
int resample(Wave& w, int quality, int samplerate)
{
float ratio = samplerate / (float) w.getRate();
- int newSizeFrames = ceil(w.getSize() * ratio);
+ int newSizeFrames = static_cast<int>(ceil(w.getSize() * ratio));
AudioBuffer newData;
newData.alloc(newSizeFrames, w.getChannels());
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.c_str(), sf_strerror(file));
+ path, sf_strerror(file));
return G_RES_ERR_IO;
}
return G_RES_OK;
}
-}}}; // giada::m::waveManager
+}}} // giada::m::waveManager
class Wave;
namespace patch
{
-class Wave;
+struct Wave;
}
namespace waveManager
{
struct Result
{
int status;
- std::unique_ptr<Wave> wave;
+ std::unique_ptr<Wave> wave = nullptr;
};
/* init
Initializes internal data. */
void init();
/* create
-Creates a new Wave object with data read from file 'path'. Takes an optional
-'id' parameter for patch persistence. */
+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=0);
+Result createFromFile(const std::string& path, ID id, int samplerate, int quality);
/* createEmpty
Creates a new silent Wave object. */
/* (de)serializeWave
Creates a new Wave given the patch raw data and vice versa. */
-std::unique_ptr<Wave> deserializeWave(const patch::Wave& w);
+std::unique_ptr<Wave> deserializeWave(const patch::Wave& w, int samplerate, int quality);
const patch::Wave serializeWave(const Wave& w);
+/* 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
int save(const Wave& w, const std::string& path);
-}}}; // giada::m::waveManager
+}}} // giada::m::waveManager
#endif
#define JUCE_APPCONFIG_H
-#ifdef _WIN32
- #include <sys/types.h>
- #include <sys/time.h>
-#endif
-
-
#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"
+++ /dev/null
-/************************************************************************/
-/*! \class RtAudio
- \brief Realtime audio i/o C++ classes.
-
- RtAudio provides a common API (Application Programming Interface)
- for realtime audio input/output across Linux (native ALSA, Jack,
- and OSS), Macintosh OS X (CoreAudio and Jack), and Windows
- (DirectSound, ASIO and WASAPI) operating systems.
-
- RtAudio GitHub site: https://github.com/thestk/rtaudio
- RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/
-
- RtAudio: realtime audio i/o C++ classes
- Copyright (c) 2001-2019 Gary P. Scavone
-
- Permission is hereby granted, free of charge, to any person
- obtaining a copy of this software and associated documentation files
- (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge,
- publish, distribute, sublicense, and/or sell copies of the Software,
- and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
-
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
-
- Any person wishing to distribute modifications to the Software is
- asked to send the modifications to the original developer so that
- they can be incorporated into the canonical version. This is,
- however, not a binding provision of this license.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-/************************************************************************/
-
-// RtAudio: Version 5.1.0
-
-#include "RtAudio.h"
-#include <iostream>
-#include <cstdlib>
-#include <cstring>
-#include <climits>
-#include <cmath>
-#include <algorithm>
-
-// Static variable definitions.
-const unsigned int RtApi::MAX_SAMPLE_RATES = 14;
-const unsigned int RtApi::SAMPLE_RATES[] = {
- 4000, 5512, 8000, 9600, 11025, 16000, 22050,
- 32000, 44100, 48000, 88200, 96000, 176400, 192000
-};
-
-#if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__)
- #define MUTEX_INITIALIZE(A) InitializeCriticalSection(A)
- #define MUTEX_DESTROY(A) DeleteCriticalSection(A)
- #define MUTEX_LOCK(A) EnterCriticalSection(A)
- #define MUTEX_UNLOCK(A) LeaveCriticalSection(A)
-
- #include "tchar.h"
-
- static std::string convertCharPointerToStdString(const char *text)
- {
- return std::string(text);
- }
-
- static std::string convertCharPointerToStdString(const wchar_t *text)
- {
- int length = WideCharToMultiByte(CP_UTF8, 0, text, -1, NULL, 0, NULL, NULL);
- std::string s( length-1, '\0' );
- WideCharToMultiByte(CP_UTF8, 0, text, -1, &s[0], length, NULL, NULL);
- return s;
- }
-
-#elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)
- // pthread API
- #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)
- #define MUTEX_DESTROY(A) pthread_mutex_destroy(A)
- #define MUTEX_LOCK(A) pthread_mutex_lock(A)
- #define MUTEX_UNLOCK(A) pthread_mutex_unlock(A)
-#else
- #define MUTEX_INITIALIZE(A) abs(*A) // dummy definitions
- #define MUTEX_DESTROY(A) abs(*A) // dummy definitions
-#endif
-
-// *************************************************** //
-//
-// RtAudio definitions.
-//
-// *************************************************** //
-
-std::string RtAudio :: getVersion( void )
-{
- return RTAUDIO_VERSION;
-}
-
-// Define API names and display names.
-// Must be in same order as API enum.
-extern "C" {
-const char* rtaudio_api_names[][2] = {
- { "unspecified" , "Unknown" },
- { "alsa" , "ALSA" },
- { "pulse" , "Pulse" },
- { "oss" , "OpenSoundSystem" },
- { "jack" , "Jack" },
- { "core" , "CoreAudio" },
- { "wasapi" , "WASAPI" },
- { "asio" , "ASIO" },
- { "ds" , "DirectSound" },
- { "dummy" , "Dummy" },
-};
-const unsigned int rtaudio_num_api_names =
- sizeof(rtaudio_api_names)/sizeof(rtaudio_api_names[0]);
-
-// The order here will control the order of RtAudio's API search in
-// the constructor.
-extern "C" const RtAudio::Api rtaudio_compiled_apis[] = {
-#if defined(__UNIX_JACK__)
- RtAudio::UNIX_JACK,
-#endif
-#if defined(__LINUX_PULSE__)
- RtAudio::LINUX_PULSE,
-#endif
-#if defined(__LINUX_ALSA__)
- RtAudio::LINUX_ALSA,
-#endif
-#if defined(__LINUX_OSS__)
- RtAudio::LINUX_OSS,
-#endif
-#if defined(__WINDOWS_ASIO__)
- RtAudio::WINDOWS_ASIO,
-#endif
-#if defined(__WINDOWS_WASAPI__)
- RtAudio::WINDOWS_WASAPI,
-#endif
-#if defined(__WINDOWS_DS__)
- RtAudio::WINDOWS_DS,
-#endif
-#if defined(__MACOSX_CORE__)
- RtAudio::MACOSX_CORE,
-#endif
-#if defined(__RTAUDIO_DUMMY__)
- RtAudio::RTAUDIO_DUMMY,
-#endif
- RtAudio::UNSPECIFIED,
-};
-extern "C" const unsigned int rtaudio_num_compiled_apis =
- sizeof(rtaudio_compiled_apis)/sizeof(rtaudio_compiled_apis[0])-1;
-}
-
-// This is a compile-time check that rtaudio_num_api_names == RtAudio::NUM_APIS.
-// If the build breaks here, check that they match.
-template<bool b> class StaticAssert { private: StaticAssert() {} };
-template<> class StaticAssert<true>{ public: StaticAssert() {} };
-class StaticAssertions { StaticAssertions() {
- StaticAssert<rtaudio_num_api_names == RtAudio::NUM_APIS>();
-}};
-
-void RtAudio :: getCompiledApi( std::vector<RtAudio::Api> &apis )
-{
- apis = std::vector<RtAudio::Api>(rtaudio_compiled_apis,
- rtaudio_compiled_apis + rtaudio_num_compiled_apis);
-}
-
-std::string RtAudio :: getApiName( RtAudio::Api api )
-{
- if (api < 0 || api >= RtAudio::NUM_APIS)
- return "";
- return rtaudio_api_names[api][0];
-}
-
-std::string RtAudio :: getApiDisplayName( RtAudio::Api api )
-{
- if (api < 0 || api >= RtAudio::NUM_APIS)
- return "Unknown";
- return rtaudio_api_names[api][1];
-}
-
-RtAudio::Api RtAudio :: getCompiledApiByName( const std::string &name )
-{
- unsigned int i=0;
- for (i = 0; i < rtaudio_num_compiled_apis; ++i)
- if (name == rtaudio_api_names[rtaudio_compiled_apis[i]][0])
- return rtaudio_compiled_apis[i];
- return RtAudio::UNSPECIFIED;
-}
-
-void RtAudio :: openRtApi( RtAudio::Api api )
-{
- if ( rtapi_ )
- delete rtapi_;
- rtapi_ = 0;
-
-#if defined(__UNIX_JACK__)
- if ( api == UNIX_JACK )
- rtapi_ = new RtApiJack();
-#endif
-#if defined(__LINUX_ALSA__)
- if ( api == LINUX_ALSA )
- rtapi_ = new RtApiAlsa();
-#endif
-#if defined(__LINUX_PULSE__)
- if ( api == LINUX_PULSE )
- rtapi_ = new RtApiPulse();
-#endif
-#if defined(__LINUX_OSS__)
- if ( api == LINUX_OSS )
- rtapi_ = new RtApiOss();
-#endif
-#if defined(__WINDOWS_ASIO__)
- if ( api == WINDOWS_ASIO )
- rtapi_ = new RtApiAsio();
-#endif
-#if defined(__WINDOWS_WASAPI__)
- if ( api == WINDOWS_WASAPI )
- rtapi_ = new RtApiWasapi();
-#endif
-#if defined(__WINDOWS_DS__)
- if ( api == WINDOWS_DS )
- rtapi_ = new RtApiDs();
-#endif
-#if defined(__MACOSX_CORE__)
- if ( api == MACOSX_CORE )
- rtapi_ = new RtApiCore();
-#endif
-#if defined(__RTAUDIO_DUMMY__)
- if ( api == RTAUDIO_DUMMY )
- rtapi_ = new RtApiDummy();
-#endif
-}
-
-RtAudio :: RtAudio( RtAudio::Api api )
-{
- rtapi_ = 0;
-
- if ( api != UNSPECIFIED ) {
- // Attempt to open the specified API.
- openRtApi( api );
- if ( rtapi_ ) return;
-
- // No compiled support for specified API value. Issue a debug
- // warning and continue as if no API was specified.
- std::cerr << "\nRtAudio: no compiled support for specified API argument!\n" << std::endl;
- }
-
- // Iterate through the compiled APIs and return as soon as we find
- // one with at least one device or we reach the end of the list.
- std::vector< RtAudio::Api > apis;
- getCompiledApi( apis );
- for ( unsigned int i=0; i<apis.size(); i++ ) {
- openRtApi( apis[i] );
- if ( rtapi_ && rtapi_->getDeviceCount() ) break;
- }
-
- if ( rtapi_ ) return;
-
- // It should not be possible to get here because the preprocessor
- // definition __RTAUDIO_DUMMY__ is automatically defined if no
- // API-specific definitions are passed to the compiler. But just in
- // case something weird happens, we'll thow an error.
- std::string errorText = "\nRtAudio: no compiled API support found ... critical error!!\n\n";
- throw( RtAudioError( errorText, RtAudioError::UNSPECIFIED ) );
-}
-
-RtAudio :: ~RtAudio()
-{
- if ( rtapi_ )
- delete rtapi_;
-}
-
-void RtAudio :: openStream( RtAudio::StreamParameters *outputParameters,
- RtAudio::StreamParameters *inputParameters,
- RtAudioFormat format, unsigned int sampleRate,
- unsigned int *bufferFrames,
- RtAudioCallback callback, void *userData,
- RtAudio::StreamOptions *options,
- RtAudioErrorCallback errorCallback )
-{
- return rtapi_->openStream( outputParameters, inputParameters, format,
- sampleRate, bufferFrames, callback,
- userData, options, errorCallback );
-}
-
-// *************************************************** //
-//
-// Public RtApi definitions (see end of file for
-// private or protected utility functions).
-//
-// *************************************************** //
-
-RtApi :: RtApi()
-{
- stream_.state = STREAM_CLOSED;
- stream_.mode = UNINITIALIZED;
- stream_.apiHandle = 0;
- stream_.userBuffer[0] = 0;
- stream_.userBuffer[1] = 0;
- MUTEX_INITIALIZE( &stream_.mutex );
- showWarnings_ = true;
- firstErrorOccurred_ = false;
-}
-
-RtApi :: ~RtApi()
-{
- MUTEX_DESTROY( &stream_.mutex );
-}
-
-void RtApi :: openStream( RtAudio::StreamParameters *oParams,
- RtAudio::StreamParameters *iParams,
- RtAudioFormat format, unsigned int sampleRate,
- unsigned int *bufferFrames,
- RtAudioCallback callback, void *userData,
- RtAudio::StreamOptions *options,
- RtAudioErrorCallback errorCallback )
-{
- if ( stream_.state != STREAM_CLOSED ) {
- errorText_ = "RtApi::openStream: a stream is already open!";
- error( RtAudioError::INVALID_USE );
- return;
- }
-
- // Clear stream information potentially left from a previously open stream.
- clearStreamInfo();
-
- if ( oParams && oParams->nChannels < 1 ) {
- errorText_ = "RtApi::openStream: a non-NULL output StreamParameters structure cannot have an nChannels value less than one.";
- error( RtAudioError::INVALID_USE );
- return;
- }
-
- if ( iParams && iParams->nChannels < 1 ) {
- errorText_ = "RtApi::openStream: a non-NULL input StreamParameters structure cannot have an nChannels value less than one.";
- error( RtAudioError::INVALID_USE );
- return;
- }
-
- if ( oParams == NULL && iParams == NULL ) {
- errorText_ = "RtApi::openStream: input and output StreamParameters structures are both NULL!";
- error( RtAudioError::INVALID_USE );
- return;
- }
-
- if ( formatBytes(format) == 0 ) {
- errorText_ = "RtApi::openStream: 'format' parameter value is undefined.";
- error( RtAudioError::INVALID_USE );
- return;
- }
-
- unsigned int nDevices = getDeviceCount();
- unsigned int oChannels = 0;
- if ( oParams ) {
- oChannels = oParams->nChannels;
- if ( oParams->deviceId >= nDevices ) {
- errorText_ = "RtApi::openStream: output device parameter value is invalid.";
- error( RtAudioError::INVALID_USE );
- return;
- }
- }
-
- unsigned int iChannels = 0;
- if ( iParams ) {
- iChannels = iParams->nChannels;
- if ( iParams->deviceId >= nDevices ) {
- errorText_ = "RtApi::openStream: input device parameter value is invalid.";
- error( RtAudioError::INVALID_USE );
- return;
- }
- }
-
- bool result;
-
- if ( oChannels > 0 ) {
-
- result = probeDeviceOpen( oParams->deviceId, OUTPUT, oChannels, oParams->firstChannel,
- sampleRate, format, bufferFrames, options );
- if ( result == false ) {
- error( RtAudioError::SYSTEM_ERROR );
- return;
- }
- }
-
- if ( iChannels > 0 ) {
-
- result = probeDeviceOpen( iParams->deviceId, INPUT, iChannels, iParams->firstChannel,
- sampleRate, format, bufferFrames, options );
- if ( result == false ) {
- if ( oChannels > 0 ) closeStream();
- error( RtAudioError::SYSTEM_ERROR );
- return;
- }
- }
-
- stream_.callbackInfo.callback = (void *) callback;
- stream_.callbackInfo.userData = userData;
- stream_.callbackInfo.errorCallback = (void *) errorCallback;
-
- if ( options ) options->numberOfBuffers = stream_.nBuffers;
- stream_.state = STREAM_STOPPED;
-}
-
-unsigned int RtApi :: getDefaultInputDevice( void )
-{
- // Should be implemented in subclasses if possible.
- return 0;
-}
-
-unsigned int RtApi :: getDefaultOutputDevice( void )
-{
- // Should be implemented in subclasses if possible.
- return 0;
-}
-
-void RtApi :: closeStream( void )
-{
- // MUST be implemented in subclasses!
- return;
-}
-
-bool RtApi :: probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/,
- unsigned int /*firstChannel*/, unsigned int /*sampleRate*/,
- RtAudioFormat /*format*/, unsigned int * /*bufferSize*/,
- RtAudio::StreamOptions * /*options*/ )
-{
- // MUST be implemented in subclasses!
- return FAILURE;
-}
-
-void RtApi :: tickStreamTime( void )
-{
- // Subclasses that do not provide their own implementation of
- // getStreamTime should call this function once per buffer I/O to
- // provide basic stream time support.
-
- stream_.streamTime += ( stream_.bufferSize * 1.0 / stream_.sampleRate );
-
-#if defined( HAVE_GETTIMEOFDAY )
- gettimeofday( &stream_.lastTickTimestamp, NULL );
-#endif
-}
-
-long RtApi :: getStreamLatency( void )
-{
- verifyStream();
-
- long totalLatency = 0;
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
- totalLatency = stream_.latency[0];
- if ( stream_.mode == INPUT || stream_.mode == DUPLEX )
- totalLatency += stream_.latency[1];
-
- return totalLatency;
-}
-
-double RtApi :: getStreamTime( void )
-{
- verifyStream();
-
-#if defined( HAVE_GETTIMEOFDAY )
- // Return a very accurate estimate of the stream time by
- // adding in the elapsed time since the last tick.
- struct timeval then;
- struct timeval now;
-
- if ( stream_.state != STREAM_RUNNING || stream_.streamTime == 0.0 )
- return stream_.streamTime;
-
- gettimeofday( &now, NULL );
- then = stream_.lastTickTimestamp;
- return stream_.streamTime +
- ((now.tv_sec + 0.000001 * now.tv_usec) -
- (then.tv_sec + 0.000001 * then.tv_usec));
-#else
- return stream_.streamTime;
-#endif
-}
-
-void RtApi :: setStreamTime( double time )
-{
- verifyStream();
-
- if ( time >= 0.0 )
- stream_.streamTime = time;
-#if defined( HAVE_GETTIMEOFDAY )
- gettimeofday( &stream_.lastTickTimestamp, NULL );
-#endif
-}
-
-unsigned int RtApi :: getStreamSampleRate( void )
-{
- verifyStream();
-
- return stream_.sampleRate;
-}
-
-
-// *************************************************** //
-//
-// OS/API-specific methods.
-//
-// *************************************************** //
-
-#if defined(__MACOSX_CORE__)
-
-// The OS X CoreAudio API is designed to use a separate callback
-// procedure for each of its audio devices. A single RtAudio duplex
-// stream using two different devices is supported here, though it
-// cannot be guaranteed to always behave correctly because we cannot
-// synchronize these two callbacks.
-//
-// A property listener is installed for over/underrun information.
-// However, no functionality is currently provided to allow property
-// listeners to trigger user handlers because it is unclear what could
-// be done if a critical stream parameter (buffer size, sample rate,
-// device disconnect) notification arrived. The listeners entail
-// quite a bit of extra code and most likely, a user program wouldn't
-// be prepared for the result anyway. However, we do provide a flag
-// to the client callback function to inform of an over/underrun.
-
-// A structure to hold various information related to the CoreAudio API
-// implementation.
-struct CoreHandle {
- AudioDeviceID id[2]; // device ids
-#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
- AudioDeviceIOProcID procId[2];
-#endif
- UInt32 iStream[2]; // device stream index (or first if using multiple)
- UInt32 nStreams[2]; // number of streams to use
- bool xrun[2];
- char *deviceBuffer;
- pthread_cond_t condition;
- int drainCounter; // Tracks callback counts when draining
- bool internalDrain; // Indicates if stop is initiated from callback or not.
-
- CoreHandle()
- :deviceBuffer(0), drainCounter(0), internalDrain(false) { nStreams[0] = 1; nStreams[1] = 1; id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
-};
-
-RtApiCore:: RtApiCore()
-{
-#if defined( AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER )
- // This is a largely undocumented but absolutely necessary
- // requirement starting with OS-X 10.6. If not called, queries and
- // updates to various audio device properties are not handled
- // correctly.
- CFRunLoopRef theRunLoop = NULL;
- AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
- OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
- if ( result != noErr ) {
- errorText_ = "RtApiCore::RtApiCore: error setting run loop property!";
- error( RtAudioError::WARNING );
- }
-#endif
-}
-
-RtApiCore :: ~RtApiCore()
-{
- // The subclass destructor gets called before the base class
- // destructor, so close an existing stream before deallocating
- // apiDeviceId memory.
- if ( stream_.state != STREAM_CLOSED ) closeStream();
-}
-
-unsigned int RtApiCore :: getDeviceCount( void )
-{
- // Find out how many audio devices there are, if any.
- UInt32 dataSize;
- AudioObjectPropertyAddress propertyAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
- OSStatus result = AudioObjectGetPropertyDataSize( kAudioObjectSystemObject, &propertyAddress, 0, NULL, &dataSize );
- if ( result != noErr ) {
- errorText_ = "RtApiCore::getDeviceCount: OS-X error getting device info!";
- error( RtAudioError::WARNING );
- return 0;
- }
-
- return dataSize / sizeof( AudioDeviceID );
-}
-
-unsigned int RtApiCore :: getDefaultInputDevice( void )
-{
- unsigned int nDevices = getDeviceCount();
- if ( nDevices <= 1 ) return 0;
-
- AudioDeviceID id;
- UInt32 dataSize = sizeof( AudioDeviceID );
- AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
- OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );
- if ( result != noErr ) {
- errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device.";
- error( RtAudioError::WARNING );
- return 0;
- }
-
- dataSize *= nDevices;
- AudioDeviceID deviceList[ nDevices ];
- property.mSelector = kAudioHardwarePropertyDevices;
- result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );
- if ( result != noErr ) {
- errorText_ = "RtApiCore::getDefaultInputDevice: OS-X system error getting device IDs.";
- error( RtAudioError::WARNING );
- return 0;
- }
-
- for ( unsigned int i=0; i<nDevices; i++ )
- if ( id == deviceList[i] ) return i;
-
- errorText_ = "RtApiCore::getDefaultInputDevice: No default device found!";
- error( RtAudioError::WARNING );
- return 0;
-}
-
-unsigned int RtApiCore :: getDefaultOutputDevice( void )
-{
- unsigned int nDevices = getDeviceCount();
- if ( nDevices <= 1 ) return 0;
-
- AudioDeviceID id;
- UInt32 dataSize = sizeof( AudioDeviceID );
- AudioObjectPropertyAddress property = { kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
- OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, &id );
- if ( result != noErr ) {
- errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device.";
- error( RtAudioError::WARNING );
- return 0;
- }
-
- dataSize = sizeof( AudioDeviceID ) * nDevices;
- AudioDeviceID deviceList[ nDevices ];
- property.mSelector = kAudioHardwarePropertyDevices;
- result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, &dataSize, (void *) &deviceList );
- if ( result != noErr ) {
- errorText_ = "RtApiCore::getDefaultOutputDevice: OS-X system error getting device IDs.";
- error( RtAudioError::WARNING );
- return 0;
- }
-
- for ( unsigned int i=0; i<nDevices; i++ )
- if ( id == deviceList[i] ) return i;
-
- errorText_ = "RtApiCore::getDefaultOutputDevice: No default device found!";
- error( RtAudioError::WARNING );
- return 0;
-}
-
-RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
-{
- RtAudio::DeviceInfo info;
- info.probed = false;
-
- // Get device ID
- unsigned int nDevices = getDeviceCount();
- if ( nDevices == 0 ) {
- errorText_ = "RtApiCore::getDeviceInfo: no devices found!";
- error( RtAudioError::INVALID_USE );
- return info;
- }
-
- if ( device >= nDevices ) {
- errorText_ = "RtApiCore::getDeviceInfo: device ID is invalid!";
- error( RtAudioError::INVALID_USE );
- return info;
- }
-
- AudioDeviceID deviceList[ nDevices ];
- UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
- AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
- OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,
- 0, NULL, &dataSize, (void *) &deviceList );
- if ( result != noErr ) {
- errorText_ = "RtApiCore::getDeviceInfo: OS-X system error getting device IDs.";
- error( RtAudioError::WARNING );
- return info;
- }
-
- AudioDeviceID id = deviceList[ device ];
-
- // Get the device name.
- info.name.erase();
- CFStringRef cfname;
- dataSize = sizeof( CFStringRef );
- property.mSelector = kAudioObjectPropertyManufacturer;
- result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device manufacturer.";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- //const char *mname = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );
- int length = CFStringGetLength(cfname);
- char *mname = (char *)malloc(length * 3 + 1);
-#if defined( UNICODE ) || defined( _UNICODE )
- CFStringGetCString(cfname, mname, length * 3 + 1, kCFStringEncodingUTF8);
-#else
- CFStringGetCString(cfname, mname, length * 3 + 1, CFStringGetSystemEncoding());
-#endif
- info.name.append( (const char *)mname, strlen(mname) );
- info.name.append( ": " );
- CFRelease( cfname );
- free(mname);
-
- property.mSelector = kAudioObjectPropertyName;
- result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &cfname );
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::probeDeviceInfo: system error (" << getErrorCode( result ) << ") getting device name.";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- //const char *name = CFStringGetCStringPtr( cfname, CFStringGetSystemEncoding() );
- length = CFStringGetLength(cfname);
- char *name = (char *)malloc(length * 3 + 1);
-#if defined( UNICODE ) || defined( _UNICODE )
- CFStringGetCString(cfname, name, length * 3 + 1, kCFStringEncodingUTF8);
-#else
- CFStringGetCString(cfname, name, length * 3 + 1, CFStringGetSystemEncoding());
-#endif
- info.name.append( (const char *)name, strlen(name) );
- CFRelease( cfname );
- free(name);
-
- // Get the output stream "configuration".
- AudioBufferList *bufferList = nil;
- property.mSelector = kAudioDevicePropertyStreamConfiguration;
- property.mScope = kAudioDevicePropertyScopeOutput;
- // property.mElement = kAudioObjectPropertyElementWildcard;
- dataSize = 0;
- result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );
- if ( result != noErr || dataSize == 0 ) {
- errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration info for device (" << device << ").";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- // Allocate the AudioBufferList.
- bufferList = (AudioBufferList *) malloc( dataSize );
- if ( bufferList == NULL ) {
- errorText_ = "RtApiCore::getDeviceInfo: memory error allocating output AudioBufferList.";
- error( RtAudioError::WARNING );
- return info;
- }
-
- result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );
- if ( result != noErr || dataSize == 0 ) {
- free( bufferList );
- errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting output stream configuration for device (" << device << ").";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- // Get output channel information.
- unsigned int i, nStreams = bufferList->mNumberBuffers;
- for ( i=0; i<nStreams; i++ )
- info.outputChannels += bufferList->mBuffers[i].mNumberChannels;
- free( bufferList );
-
- // Get the input stream "configuration".
- property.mScope = kAudioDevicePropertyScopeInput;
- result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );
- if ( result != noErr || dataSize == 0 ) {
- errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration info for device (" << device << ").";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- // Allocate the AudioBufferList.
- bufferList = (AudioBufferList *) malloc( dataSize );
- if ( bufferList == NULL ) {
- errorText_ = "RtApiCore::getDeviceInfo: memory error allocating input AudioBufferList.";
- error( RtAudioError::WARNING );
- return info;
- }
-
- result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );
- if (result != noErr || dataSize == 0) {
- free( bufferList );
- errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting input stream configuration for device (" << device << ").";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- // Get input channel information.
- nStreams = bufferList->mNumberBuffers;
- for ( i=0; i<nStreams; i++ )
- info.inputChannels += bufferList->mBuffers[i].mNumberChannels;
- free( bufferList );
-
- // If device opens for both playback and capture, we determine the channels.
- if ( info.outputChannels > 0 && info.inputChannels > 0 )
- info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
-
- // Probe the device sample rates.
- bool isInput = false;
- if ( info.outputChannels == 0 ) isInput = true;
-
- // Determine the supported sample rates.
- property.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
- if ( isInput == false ) property.mScope = kAudioDevicePropertyScopeOutput;
- result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );
- if ( result != kAudioHardwareNoError || dataSize == 0 ) {
- errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rate info.";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- UInt32 nRanges = dataSize / sizeof( AudioValueRange );
- AudioValueRange rangeList[ nRanges ];
- result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &rangeList );
- if ( result != kAudioHardwareNoError ) {
- errorStream_ << "RtApiCore::getDeviceInfo: system error (" << getErrorCode( result ) << ") getting sample rates.";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- // The sample rate reporting mechanism is a bit of a mystery. It
- // seems that it can either return individual rates or a range of
- // rates. I assume that if the min / max range values are the same,
- // then that represents a single supported rate and if the min / max
- // range values are different, the device supports an arbitrary
- // range of values (though there might be multiple ranges, so we'll
- // use the most conservative range).
- Float64 minimumRate = 1.0, maximumRate = 10000000000.0;
- bool haveValueRange = false;
- info.sampleRates.clear();
- for ( UInt32 i=0; i<nRanges; i++ ) {
- if ( rangeList[i].mMinimum == rangeList[i].mMaximum ) {
- unsigned int tmpSr = (unsigned int) rangeList[i].mMinimum;
- info.sampleRates.push_back( tmpSr );
-
- if ( !info.preferredSampleRate || ( tmpSr <= 48000 && tmpSr > info.preferredSampleRate ) )
- info.preferredSampleRate = tmpSr;
-
- } else {
- haveValueRange = true;
- if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum;
- if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum;
- }
- }
-
- if ( haveValueRange ) {
- for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
- if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) {
- info.sampleRates.push_back( SAMPLE_RATES[k] );
-
- if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
- info.preferredSampleRate = SAMPLE_RATES[k];
- }
- }
- }
-
- // Sort and remove any redundant values
- std::sort( info.sampleRates.begin(), info.sampleRates.end() );
- info.sampleRates.erase( unique( info.sampleRates.begin(), info.sampleRates.end() ), info.sampleRates.end() );
-
- if ( info.sampleRates.size() == 0 ) {
- errorStream_ << "RtApiCore::probeDeviceInfo: No supported sample rates found for device (" << device << ").";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- // CoreAudio always uses 32-bit floating point data for PCM streams.
- // Thus, any other "physical" formats supported by the device are of
- // no interest to the client.
- info.nativeFormats = RTAUDIO_FLOAT32;
-
- if ( info.outputChannels > 0 )
- if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;
- if ( info.inputChannels > 0 )
- if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;
-
- info.probed = true;
- return info;
-}
-
-static OSStatus callbackHandler( AudioDeviceID inDevice,
- const AudioTimeStamp* /*inNow*/,
- const AudioBufferList* inInputData,
- const AudioTimeStamp* /*inInputTime*/,
- AudioBufferList* outOutputData,
- const AudioTimeStamp* /*inOutputTime*/,
- void* infoPointer )
-{
- CallbackInfo *info = (CallbackInfo *) infoPointer;
-
- RtApiCore *object = (RtApiCore *) info->object;
- if ( object->callbackEvent( inDevice, inInputData, outOutputData ) == false )
- return kAudioHardwareUnspecifiedError;
- else
- return kAudioHardwareNoError;
-}
-
-static OSStatus xrunListener( AudioObjectID /*inDevice*/,
- UInt32 nAddresses,
- const AudioObjectPropertyAddress properties[],
- void* handlePointer )
-{
- CoreHandle *handle = (CoreHandle *) handlePointer;
- for ( UInt32 i=0; i<nAddresses; i++ ) {
- if ( properties[i].mSelector == kAudioDeviceProcessorOverload ) {
- if ( properties[i].mScope == kAudioDevicePropertyScopeInput )
- handle->xrun[1] = true;
- else
- handle->xrun[0] = true;
- }
- }
-
- return kAudioHardwareNoError;
-}
-
-static OSStatus rateListener( AudioObjectID inDevice,
- UInt32 /*nAddresses*/,
- const AudioObjectPropertyAddress /*properties*/[],
- void* ratePointer )
-{
- Float64 *rate = (Float64 *) ratePointer;
- UInt32 dataSize = sizeof( Float64 );
- AudioObjectPropertyAddress property = { kAudioDevicePropertyNominalSampleRate,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
- AudioObjectGetPropertyData( inDevice, &property, 0, NULL, &dataSize, rate );
- return kAudioHardwareNoError;
-}
-
-bool RtApiCore :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
- RtAudioFormat format, unsigned int *bufferSize,
- RtAudio::StreamOptions *options )
-{
- // Get device ID
- unsigned int nDevices = getDeviceCount();
- if ( nDevices == 0 ) {
- // This should not happen because a check is made before this function is called.
- errorText_ = "RtApiCore::probeDeviceOpen: no devices found!";
- return FAILURE;
- }
-
- if ( device >= nDevices ) {
- // This should not happen because a check is made before this function is called.
- errorText_ = "RtApiCore::probeDeviceOpen: device ID is invalid!";
- return FAILURE;
- }
-
- AudioDeviceID deviceList[ nDevices ];
- UInt32 dataSize = sizeof( AudioDeviceID ) * nDevices;
- AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
- OSStatus result = AudioObjectGetPropertyData( kAudioObjectSystemObject, &property,
- 0, NULL, &dataSize, (void *) &deviceList );
- if ( result != noErr ) {
- errorText_ = "RtApiCore::probeDeviceOpen: OS-X system error getting device IDs.";
- return FAILURE;
- }
-
- AudioDeviceID id = deviceList[ device ];
-
- // Setup for stream mode.
- bool isInput = false;
- if ( mode == INPUT ) {
- isInput = true;
- property.mScope = kAudioDevicePropertyScopeInput;
- }
- else
- property.mScope = kAudioDevicePropertyScopeOutput;
-
- // Get the stream "configuration".
- AudioBufferList *bufferList = nil;
- dataSize = 0;
- property.mSelector = kAudioDevicePropertyStreamConfiguration;
- result = AudioObjectGetPropertyDataSize( id, &property, 0, NULL, &dataSize );
- if ( result != noErr || dataSize == 0 ) {
- errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration info for device (" << device << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Allocate the AudioBufferList.
- bufferList = (AudioBufferList *) malloc( dataSize );
- if ( bufferList == NULL ) {
- errorText_ = "RtApiCore::probeDeviceOpen: memory error allocating AudioBufferList.";
- return FAILURE;
- }
-
- result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, bufferList );
- if (result != noErr || dataSize == 0) {
- free( bufferList );
- errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream configuration for device (" << device << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Search for one or more streams that contain the desired number of
- // channels. CoreAudio devices can have an arbitrary number of
- // streams and each stream can have an arbitrary number of channels.
- // For each stream, a single buffer of interleaved samples is
- // provided. RtAudio prefers the use of one stream of interleaved
- // data or multiple consecutive single-channel streams. However, we
- // now support multiple consecutive multi-channel streams of
- // interleaved data as well.
- UInt32 iStream, offsetCounter = firstChannel;
- UInt32 nStreams = bufferList->mNumberBuffers;
- bool monoMode = false;
- bool foundStream = false;
-
- // First check that the device supports the requested number of
- // channels.
- UInt32 deviceChannels = 0;
- for ( iStream=0; iStream<nStreams; iStream++ )
- deviceChannels += bufferList->mBuffers[iStream].mNumberChannels;
-
- if ( deviceChannels < ( channels + firstChannel ) ) {
- free( bufferList );
- errorStream_ << "RtApiCore::probeDeviceOpen: the device (" << device << ") does not support the requested channel count.";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Look for a single stream meeting our needs.
- UInt32 firstStream, streamCount = 1, streamChannels = 0, channelOffset = 0;
- for ( iStream=0; iStream<nStreams; iStream++ ) {
- streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
- if ( streamChannels >= channels + offsetCounter ) {
- firstStream = iStream;
- channelOffset = offsetCounter;
- foundStream = true;
- break;
- }
- if ( streamChannels > offsetCounter ) break;
- offsetCounter -= streamChannels;
- }
-
- // If we didn't find a single stream above, then we should be able
- // to meet the channel specification with multiple streams.
- if ( foundStream == false ) {
- monoMode = true;
- offsetCounter = firstChannel;
- for ( iStream=0; iStream<nStreams; iStream++ ) {
- streamChannels = bufferList->mBuffers[iStream].mNumberChannels;
- if ( streamChannels > offsetCounter ) break;
- offsetCounter -= streamChannels;
- }
-
- firstStream = iStream;
- channelOffset = offsetCounter;
- Int32 channelCounter = channels + offsetCounter - streamChannels;
-
- if ( streamChannels > 1 ) monoMode = false;
- while ( channelCounter > 0 ) {
- streamChannels = bufferList->mBuffers[++iStream].mNumberChannels;
- if ( streamChannels > 1 ) monoMode = false;
- channelCounter -= streamChannels;
- streamCount++;
- }
- }
-
- free( bufferList );
-
- // Determine the buffer size.
- AudioValueRange bufferRange;
- dataSize = sizeof( AudioValueRange );
- property.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
- result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &bufferRange );
-
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting buffer size range for device (" << device << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- if ( bufferRange.mMinimum > *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMinimum;
- else if ( bufferRange.mMaximum < *bufferSize ) *bufferSize = (unsigned long) bufferRange.mMaximum;
- if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) *bufferSize = (unsigned long) bufferRange.mMinimum;
-
- // Set the buffer size. For multiple streams, I'm assuming we only
- // need to make this setting for the master channel.
- UInt32 theSize = (UInt32) *bufferSize;
- dataSize = sizeof( UInt32 );
- property.mSelector = kAudioDevicePropertyBufferFrameSize;
- result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &theSize );
-
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting the buffer size for device (" << device << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // If attempting to setup a duplex stream, the bufferSize parameter
- // MUST be the same in both directions!
- *bufferSize = theSize;
- if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {
- errorStream_ << "RtApiCore::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << device << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- stream_.bufferSize = *bufferSize;
- stream_.nBuffers = 1;
-
- // Try to set "hog" mode ... it's not clear to me this is working.
- if ( options && options->flags & RTAUDIO_HOG_DEVICE ) {
- pid_t hog_pid;
- dataSize = sizeof( hog_pid );
- property.mSelector = kAudioDevicePropertyHogMode;
- result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &hog_pid );
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting 'hog' state!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- if ( hog_pid != getpid() ) {
- hog_pid = getpid();
- result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &hog_pid );
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting 'hog' state!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- }
- }
-
- // Check and if necessary, change the sample rate for the device.
- Float64 nominalRate;
- dataSize = sizeof( Float64 );
- property.mSelector = kAudioDevicePropertyNominalSampleRate;
- result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &nominalRate );
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting current sample rate.";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Only change the sample rate if off by more than 1 Hz.
- if ( fabs( nominalRate - (double)sampleRate ) > 1.0 ) {
-
- // Set a property listener for the sample rate change
- Float64 reportedRate = 0.0;
- AudioObjectPropertyAddress tmp = { kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
- result = AudioObjectAddPropertyListener( id, &tmp, rateListener, (void *) &reportedRate );
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate property listener for device (" << device << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- nominalRate = (Float64) sampleRate;
- result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &nominalRate );
- if ( result != noErr ) {
- AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );
- errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate for device (" << device << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Now wait until the reported nominal rate is what we just set.
- UInt32 microCounter = 0;
- while ( reportedRate != nominalRate ) {
- microCounter += 5000;
- if ( microCounter > 5000000 ) break;
- usleep( 5000 );
- }
-
- // Remove the property listener.
- AudioObjectRemovePropertyListener( id, &tmp, rateListener, (void *) &reportedRate );
-
- if ( microCounter > 5000000 ) {
- errorStream_ << "RtApiCore::probeDeviceOpen: timeout waiting for sample rate update for device (" << device << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- }
-
- // Now set the stream format for all streams. Also, check the
- // physical format of the device and change that if necessary.
- AudioStreamBasicDescription description;
- dataSize = sizeof( AudioStreamBasicDescription );
- property.mSelector = kAudioStreamPropertyVirtualFormat;
- result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description );
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream format for device (" << device << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Set the sample rate and data format id. However, only make the
- // change if the sample rate is not within 1.0 of the desired
- // rate and the format is not linear pcm.
- bool updateFormat = false;
- if ( fabs( description.mSampleRate - (Float64)sampleRate ) > 1.0 ) {
- description.mSampleRate = (Float64) sampleRate;
- updateFormat = true;
- }
-
- if ( description.mFormatID != kAudioFormatLinearPCM ) {
- description.mFormatID = kAudioFormatLinearPCM;
- updateFormat = true;
- }
-
- if ( updateFormat ) {
- result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &description );
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting sample rate or data format for device (" << device << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- }
-
- // Now check the physical format.
- property.mSelector = kAudioStreamPropertyPhysicalFormat;
- result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &description );
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting stream physical format for device (" << device << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- //std::cout << "Current physical stream format:" << std::endl;
- //std::cout << " mBitsPerChan = " << description.mBitsPerChannel << std::endl;
- //std::cout << " aligned high = " << (description.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (description.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;
- //std::cout << " bytesPerFrame = " << description.mBytesPerFrame << std::endl;
- //std::cout << " sample rate = " << description.mSampleRate << std::endl;
-
- if ( description.mFormatID != kAudioFormatLinearPCM || description.mBitsPerChannel < 16 ) {
- description.mFormatID = kAudioFormatLinearPCM;
- //description.mSampleRate = (Float64) sampleRate;
- AudioStreamBasicDescription testDescription = description;
- UInt32 formatFlags;
-
- // We'll try higher bit rates first and then work our way down.
- std::vector< std::pair<UInt32, UInt32> > physicalFormats;
- formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsFloat) & ~kLinearPCMFormatFlagIsSignedInteger;
- physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );
- formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;
- physicalFormats.push_back( std::pair<Float32, UInt32>( 32, formatFlags ) );
- physicalFormats.push_back( std::pair<Float32, UInt32>( 24, formatFlags ) ); // 24-bit packed
- formatFlags &= ~( kAudioFormatFlagIsPacked | kAudioFormatFlagIsAlignedHigh );
- physicalFormats.push_back( std::pair<Float32, UInt32>( 24.2, formatFlags ) ); // 24-bit in 4 bytes, aligned low
- formatFlags |= kAudioFormatFlagIsAlignedHigh;
- physicalFormats.push_back( std::pair<Float32, UInt32>( 24.4, formatFlags ) ); // 24-bit in 4 bytes, aligned high
- formatFlags = (description.mFormatFlags | kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked) & ~kLinearPCMFormatFlagIsFloat;
- physicalFormats.push_back( std::pair<Float32, UInt32>( 16, formatFlags ) );
- physicalFormats.push_back( std::pair<Float32, UInt32>( 8, formatFlags ) );
-
- bool setPhysicalFormat = false;
- for( unsigned int i=0; i<physicalFormats.size(); i++ ) {
- testDescription = description;
- testDescription.mBitsPerChannel = (UInt32) physicalFormats[i].first;
- testDescription.mFormatFlags = physicalFormats[i].second;
- if ( (24 == (UInt32)physicalFormats[i].first) && ~( physicalFormats[i].second & kAudioFormatFlagIsPacked ) )
- testDescription.mBytesPerFrame = 4 * testDescription.mChannelsPerFrame;
- else
- testDescription.mBytesPerFrame = testDescription.mBitsPerChannel/8 * testDescription.mChannelsPerFrame;
- testDescription.mBytesPerPacket = testDescription.mBytesPerFrame * testDescription.mFramesPerPacket;
- result = AudioObjectSetPropertyData( id, &property, 0, NULL, dataSize, &testDescription );
- if ( result == noErr ) {
- setPhysicalFormat = true;
- //std::cout << "Updated physical stream format:" << std::endl;
- //std::cout << " mBitsPerChan = " << testDescription.mBitsPerChannel << std::endl;
- //std::cout << " aligned high = " << (testDescription.mFormatFlags & kAudioFormatFlagIsAlignedHigh) << ", isPacked = " << (testDescription.mFormatFlags & kAudioFormatFlagIsPacked) << std::endl;
- //std::cout << " bytesPerFrame = " << testDescription.mBytesPerFrame << std::endl;
- //std::cout << " sample rate = " << testDescription.mSampleRate << std::endl;
- break;
- }
- }
-
- if ( !setPhysicalFormat ) {
- errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") setting physical data format for device (" << device << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- } // done setting virtual/physical formats.
-
- // Get the stream / device latency.
- UInt32 latency;
- dataSize = sizeof( UInt32 );
- property.mSelector = kAudioDevicePropertyLatency;
- if ( AudioObjectHasProperty( id, &property ) == true ) {
- result = AudioObjectGetPropertyData( id, &property, 0, NULL, &dataSize, &latency );
- if ( result == kAudioHardwareNoError ) stream_.latency[ mode ] = latency;
- else {
- errorStream_ << "RtApiCore::probeDeviceOpen: system error (" << getErrorCode( result ) << ") getting device latency for device (" << device << ").";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- }
- }
-
- // Byte-swapping: According to AudioHardware.h, the stream data will
- // always be presented in native-endian format, so we should never
- // need to byte swap.
- stream_.doByteSwap[mode] = false;
-
- // From the CoreAudio documentation, PCM data must be supplied as
- // 32-bit floats.
- stream_.userFormat = format;
- stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
-
- if ( streamCount == 1 )
- stream_.nDeviceChannels[mode] = description.mChannelsPerFrame;
- else // multiple streams
- stream_.nDeviceChannels[mode] = channels;
- stream_.nUserChannels[mode] = channels;
- stream_.channelOffset[mode] = channelOffset; // offset within a CoreAudio stream
- if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
- else stream_.userInterleaved = true;
- stream_.deviceInterleaved[mode] = true;
- if ( monoMode == true ) stream_.deviceInterleaved[mode] = false;
-
- // Set flags for buffer conversion.
- stream_.doConvertBuffer[mode] = false;
- if ( stream_.userFormat != stream_.deviceFormat[mode] )
- stream_.doConvertBuffer[mode] = true;
- if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
- stream_.doConvertBuffer[mode] = true;
- if ( streamCount == 1 ) {
- if ( stream_.nUserChannels[mode] > 1 &&
- stream_.userInterleaved != stream_.deviceInterleaved[mode] )
- stream_.doConvertBuffer[mode] = true;
- }
- else if ( monoMode && stream_.userInterleaved )
- stream_.doConvertBuffer[mode] = true;
-
- // Allocate our CoreHandle structure for the stream.
- CoreHandle *handle = 0;
- if ( stream_.apiHandle == 0 ) {
- try {
- handle = new CoreHandle;
- }
- catch ( std::bad_alloc& ) {
- errorText_ = "RtApiCore::probeDeviceOpen: error allocating CoreHandle memory.";
- goto error;
- }
-
- if ( pthread_cond_init( &handle->condition, NULL ) ) {
- errorText_ = "RtApiCore::probeDeviceOpen: error initializing pthread condition variable.";
- goto error;
- }
- stream_.apiHandle = (void *) handle;
- }
- else
- handle = (CoreHandle *) stream_.apiHandle;
- handle->iStream[mode] = firstStream;
- handle->nStreams[mode] = streamCount;
- handle->id[mode] = id;
-
- // Allocate necessary internal buffers.
- unsigned long bufferBytes;
- bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
- // stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
- stream_.userBuffer[mode] = (char *) malloc( bufferBytes * sizeof(char) );
- memset( stream_.userBuffer[mode], 0, bufferBytes * sizeof(char) );
- if ( stream_.userBuffer[mode] == NULL ) {
- errorText_ = "RtApiCore::probeDeviceOpen: error allocating user buffer memory.";
- goto error;
- }
-
- // If possible, we will make use of the CoreAudio stream buffers as
- // "device buffers". However, we can't do this if using multiple
- // streams.
- if ( stream_.doConvertBuffer[mode] && handle->nStreams[mode] > 1 ) {
-
- bool makeBuffer = true;
- bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
- if ( mode == INPUT ) {
- if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
- unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
- if ( bufferBytes <= bytesOut ) makeBuffer = false;
- }
- }
-
- if ( makeBuffer ) {
- bufferBytes *= *bufferSize;
- if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
- stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
- if ( stream_.deviceBuffer == NULL ) {
- errorText_ = "RtApiCore::probeDeviceOpen: error allocating device buffer memory.";
- goto error;
- }
- }
- }
-
- stream_.sampleRate = sampleRate;
- stream_.device[mode] = device;
- stream_.state = STREAM_STOPPED;
- stream_.callbackInfo.object = (void *) this;
-
- // Setup the buffer conversion information structure.
- if ( stream_.doConvertBuffer[mode] ) {
- if ( streamCount > 1 ) setConvertInfo( mode, 0 );
- else setConvertInfo( mode, channelOffset );
- }
-
- if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device )
- // Only one callback procedure per device.
- stream_.mode = DUPLEX;
- else {
-#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
- result = AudioDeviceCreateIOProcID( id, callbackHandler, (void *) &stream_.callbackInfo, &handle->procId[mode] );
-#else
- // deprecated in favor of AudioDeviceCreateIOProcID()
- result = AudioDeviceAddIOProc( id, callbackHandler, (void *) &stream_.callbackInfo );
-#endif
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::probeDeviceOpen: system error setting callback for device (" << device << ").";
- errorText_ = errorStream_.str();
- goto error;
- }
- if ( stream_.mode == OUTPUT && mode == INPUT )
- stream_.mode = DUPLEX;
- else
- stream_.mode = mode;
- }
-
- // Setup the device property listener for over/underload.
- property.mSelector = kAudioDeviceProcessorOverload;
- property.mScope = kAudioObjectPropertyScopeGlobal;
- result = AudioObjectAddPropertyListener( id, &property, xrunListener, (void *) handle );
-
- return SUCCESS;
-
- error:
- if ( handle ) {
- pthread_cond_destroy( &handle->condition );
- delete handle;
- stream_.apiHandle = 0;
- }
-
- for ( int i=0; i<2; i++ ) {
- if ( stream_.userBuffer[i] ) {
- free( stream_.userBuffer[i] );
- stream_.userBuffer[i] = 0;
- }
- }
-
- if ( stream_.deviceBuffer ) {
- free( stream_.deviceBuffer );
- stream_.deviceBuffer = 0;
- }
-
- stream_.state = STREAM_CLOSED;
- return FAILURE;
-}
-
-void RtApiCore :: closeStream( void )
-{
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiCore::closeStream(): no open stream to close!";
- error( RtAudioError::WARNING );
- return;
- }
-
- CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
- if (handle) {
- AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
-
- property.mSelector = kAudioDeviceProcessorOverload;
- property.mScope = kAudioObjectPropertyScopeGlobal;
- if (AudioObjectRemovePropertyListener( handle->id[0], &property, xrunListener, (void *) handle ) != noErr) {
- errorText_ = "RtApiCore::closeStream(): error removing property listener!";
- error( RtAudioError::WARNING );
- }
-
-#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
- if ( stream_.state == STREAM_RUNNING )
- AudioDeviceStop( handle->id[0], handle->procId[0] );
- AudioDeviceDestroyIOProcID( handle->id[0], handle->procId[0] );
-#else // deprecated behaviour
- if ( stream_.state == STREAM_RUNNING )
- AudioDeviceStop( handle->id[0], callbackHandler );
- AudioDeviceRemoveIOProc( handle->id[0], callbackHandler );
-#endif
- }
- }
-
- if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
- if (handle) {
- AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster };
-
- property.mSelector = kAudioDeviceProcessorOverload;
- property.mScope = kAudioObjectPropertyScopeGlobal;
- if (AudioObjectRemovePropertyListener( handle->id[1], &property, xrunListener, (void *) handle ) != noErr) {
- errorText_ = "RtApiCore::closeStream(): error removing property listener!";
- error( RtAudioError::WARNING );
- }
-
-#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
- if ( stream_.state == STREAM_RUNNING )
- AudioDeviceStop( handle->id[1], handle->procId[1] );
- AudioDeviceDestroyIOProcID( handle->id[1], handle->procId[1] );
-#else // deprecated behaviour
- if ( stream_.state == STREAM_RUNNING )
- AudioDeviceStop( handle->id[1], callbackHandler );
- AudioDeviceRemoveIOProc( handle->id[1], callbackHandler );
-#endif
- }
- }
-
- for ( int i=0; i<2; i++ ) {
- if ( stream_.userBuffer[i] ) {
- free( stream_.userBuffer[i] );
- stream_.userBuffer[i] = 0;
- }
- }
-
- if ( stream_.deviceBuffer ) {
- free( stream_.deviceBuffer );
- stream_.deviceBuffer = 0;
- }
-
- // Destroy pthread condition variable.
- pthread_cond_destroy( &handle->condition );
- delete handle;
- stream_.apiHandle = 0;
-
- stream_.mode = UNINITIALIZED;
- stream_.state = STREAM_CLOSED;
-}
-
-void RtApiCore :: startStream( void )
-{
- verifyStream();
- if ( stream_.state == STREAM_RUNNING ) {
- errorText_ = "RtApiCore::startStream(): the stream is already running!";
- error( RtAudioError::WARNING );
- return;
- }
-
-#if defined( HAVE_GETTIMEOFDAY )
- gettimeofday( &stream_.lastTickTimestamp, NULL );
-#endif
-
- OSStatus result = noErr;
- CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
-
-#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
- result = AudioDeviceStart( handle->id[0], handle->procId[0] );
-#else // deprecated behaviour
- result = AudioDeviceStart( handle->id[0], callbackHandler );
-#endif
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::startStream: system error (" << getErrorCode( result ) << ") starting callback procedure on device (" << stream_.device[0] << ").";
- errorText_ = errorStream_.str();
- goto unlock;
- }
- }
-
- if ( stream_.mode == INPUT ||
- ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
-
-#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
- result = AudioDeviceStart( handle->id[1], handle->procId[1] );
-#else // deprecated behaviour
- result = AudioDeviceStart( handle->id[1], callbackHandler );
-#endif
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::startStream: system error starting input callback procedure on device (" << stream_.device[1] << ").";
- errorText_ = errorStream_.str();
- goto unlock;
- }
- }
-
- handle->drainCounter = 0;
- handle->internalDrain = false;
- stream_.state = STREAM_RUNNING;
-
- unlock:
- if ( result == noErr ) return;
- error( RtAudioError::SYSTEM_ERROR );
-}
-
-void RtApiCore :: stopStream( void )
-{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiCore::stopStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
- }
-
- OSStatus result = noErr;
- CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
-
- if ( handle->drainCounter == 0 ) {
- handle->drainCounter = 2;
- pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
- }
-
-#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
- result = AudioDeviceStop( handle->id[0], handle->procId[0] );
-#else // deprecated behaviour
- result = AudioDeviceStop( handle->id[0], callbackHandler );
-#endif
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping callback procedure on device (" << stream_.device[0] << ").";
- errorText_ = errorStream_.str();
- goto unlock;
- }
- }
-
- if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
-
-#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
- result = AudioDeviceStop( handle->id[0], handle->procId[1] );
-#else // deprecated behaviour
- result = AudioDeviceStop( handle->id[1], callbackHandler );
-#endif
- if ( result != noErr ) {
- errorStream_ << "RtApiCore::stopStream: system error (" << getErrorCode( result ) << ") stopping input callback procedure on device (" << stream_.device[1] << ").";
- errorText_ = errorStream_.str();
- goto unlock;
- }
- }
-
- stream_.state = STREAM_STOPPED;
-
- unlock:
- if ( result == noErr ) return;
- error( RtAudioError::SYSTEM_ERROR );
-}
-
-void RtApiCore :: abortStream( void )
-{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiCore::abortStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
- }
-
- CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
- handle->drainCounter = 2;
-
- stopStream();
-}
-
-// This function will be called by a spawned thread when the user
-// callback function signals that the stream should be stopped or
-// aborted. It is better to handle it this way because the
-// callbackEvent() function probably should return before the AudioDeviceStop()
-// function is called.
-static void *coreStopStream( void *ptr )
-{
- CallbackInfo *info = (CallbackInfo *) ptr;
- RtApiCore *object = (RtApiCore *) info->object;
-
- object->stopStream();
- pthread_exit( NULL );
-}
-
-bool RtApiCore :: callbackEvent( AudioDeviceID deviceId,
- const AudioBufferList *inBufferList,
- const AudioBufferList *outBufferList )
-{
- if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
- error( RtAudioError::WARNING );
- return FAILURE;
- }
-
- CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
- CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
-
- // Check if we were draining the stream and signal is finished.
- if ( handle->drainCounter > 3 ) {
- ThreadHandle threadId;
-
- stream_.state = STREAM_STOPPING;
- if ( handle->internalDrain == true )
- pthread_create( &threadId, NULL, coreStopStream, info );
- else // external call to stopStream()
- pthread_cond_signal( &handle->condition );
- return SUCCESS;
- }
-
- AudioDeviceID outputDevice = handle->id[0];
-
- // Invoke user callback to get fresh output data UNLESS we are
- // draining stream or duplex mode AND the input/output devices are
- // different AND this function is called for the input device.
- if ( handle->drainCounter == 0 && ( stream_.mode != DUPLEX || deviceId == outputDevice ) ) {
- RtAudioCallback callback = (RtAudioCallback) info->callback;
- double streamTime = getStreamTime();
- RtAudioStreamStatus status = 0;
- if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
- status |= RTAUDIO_OUTPUT_UNDERFLOW;
- handle->xrun[0] = false;
- }
- if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
- status |= RTAUDIO_INPUT_OVERFLOW;
- handle->xrun[1] = false;
- }
-
- int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
- stream_.bufferSize, streamTime, status, info->userData );
- if ( cbReturnValue == 2 ) {
- stream_.state = STREAM_STOPPING;
- handle->drainCounter = 2;
- abortStream();
- return SUCCESS;
- }
- else if ( cbReturnValue == 1 ) {
- handle->drainCounter = 1;
- handle->internalDrain = true;
- }
- }
-
- if ( stream_.mode == OUTPUT || ( stream_.mode == DUPLEX && deviceId == outputDevice ) ) {
-
- if ( handle->drainCounter > 1 ) { // write zeros to the output stream
-
- if ( handle->nStreams[0] == 1 ) {
- memset( outBufferList->mBuffers[handle->iStream[0]].mData,
- 0,
- outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
- }
- else { // fill multiple streams with zeros
- for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {
- memset( outBufferList->mBuffers[handle->iStream[0]+i].mData,
- 0,
- outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize );
- }
- }
- }
- else if ( handle->nStreams[0] == 1 ) {
- if ( stream_.doConvertBuffer[0] ) { // convert directly to CoreAudio stream buffer
- convertBuffer( (char *) outBufferList->mBuffers[handle->iStream[0]].mData,
- stream_.userBuffer[0], stream_.convertInfo[0] );
- }
- else { // copy from user buffer
- memcpy( outBufferList->mBuffers[handle->iStream[0]].mData,
- stream_.userBuffer[0],
- outBufferList->mBuffers[handle->iStream[0]].mDataByteSize );
- }
- }
- else { // fill multiple streams
- Float32 *inBuffer = (Float32 *) stream_.userBuffer[0];
- if ( stream_.doConvertBuffer[0] ) {
- convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
- inBuffer = (Float32 *) stream_.deviceBuffer;
- }
-
- if ( stream_.deviceInterleaved[0] == false ) { // mono mode
- UInt32 bufferBytes = outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
- for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
- memcpy( outBufferList->mBuffers[handle->iStream[0]+i].mData,
- (void *)&inBuffer[i*stream_.bufferSize], bufferBytes );
- }
- }
- else { // fill multiple multi-channel streams with interleaved data
- UInt32 streamChannels, channelsLeft, inJump, outJump, inOffset;
- Float32 *out, *in;
-
- bool inInterleaved = ( stream_.userInterleaved ) ? true : false;
- UInt32 inChannels = stream_.nUserChannels[0];
- if ( stream_.doConvertBuffer[0] ) {
- inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode
- inChannels = stream_.nDeviceChannels[0];
- }
-
- if ( inInterleaved ) inOffset = 1;
- else inOffset = stream_.bufferSize;
-
- channelsLeft = inChannels;
- for ( unsigned int i=0; i<handle->nStreams[0]; i++ ) {
- in = inBuffer;
- out = (Float32 *) outBufferList->mBuffers[handle->iStream[0]+i].mData;
- streamChannels = outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels;
-
- outJump = 0;
- // Account for possible channel offset in first stream
- if ( i == 0 && stream_.channelOffset[0] > 0 ) {
- streamChannels -= stream_.channelOffset[0];
- outJump = stream_.channelOffset[0];
- out += outJump;
- }
-
- // Account for possible unfilled channels at end of the last stream
- if ( streamChannels > channelsLeft ) {
- outJump = streamChannels - channelsLeft;
- streamChannels = channelsLeft;
- }
-
- // Determine input buffer offsets and skips
- if ( inInterleaved ) {
- inJump = inChannels;
- in += inChannels - channelsLeft;
- }
- else {
- inJump = 1;
- in += (inChannels - channelsLeft) * inOffset;
- }
-
- for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {
- for ( unsigned int j=0; j<streamChannels; j++ ) {
- *out++ = in[j*inOffset];
- }
- out += outJump;
- in += inJump;
- }
- channelsLeft -= streamChannels;
- }
- }
- }
- }
-
- // Don't bother draining input
- if ( handle->drainCounter ) {
- handle->drainCounter++;
- goto unlock;
- }
-
- AudioDeviceID inputDevice;
- inputDevice = handle->id[1];
- if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && deviceId == inputDevice ) ) {
-
- if ( handle->nStreams[1] == 1 ) {
- if ( stream_.doConvertBuffer[1] ) { // convert directly from CoreAudio stream buffer
- convertBuffer( stream_.userBuffer[1],
- (char *) inBufferList->mBuffers[handle->iStream[1]].mData,
- stream_.convertInfo[1] );
- }
- else { // copy to user buffer
- memcpy( stream_.userBuffer[1],
- inBufferList->mBuffers[handle->iStream[1]].mData,
- inBufferList->mBuffers[handle->iStream[1]].mDataByteSize );
- }
- }
- else { // read from multiple streams
- Float32 *outBuffer = (Float32 *) stream_.userBuffer[1];
- if ( stream_.doConvertBuffer[1] ) outBuffer = (Float32 *) stream_.deviceBuffer;
-
- if ( stream_.deviceInterleaved[1] == false ) { // mono mode
- UInt32 bufferBytes = inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;
- for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
- memcpy( (void *)&outBuffer[i*stream_.bufferSize],
- inBufferList->mBuffers[handle->iStream[1]+i].mData, bufferBytes );
- }
- }
- else { // read from multiple multi-channel streams
- UInt32 streamChannels, channelsLeft, inJump, outJump, outOffset;
- Float32 *out, *in;
-
- bool outInterleaved = ( stream_.userInterleaved ) ? true : false;
- UInt32 outChannels = stream_.nUserChannels[1];
- if ( stream_.doConvertBuffer[1] ) {
- outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode
- outChannels = stream_.nDeviceChannels[1];
- }
-
- if ( outInterleaved ) outOffset = 1;
- else outOffset = stream_.bufferSize;
-
- channelsLeft = outChannels;
- for ( unsigned int i=0; i<handle->nStreams[1]; i++ ) {
- out = outBuffer;
- in = (Float32 *) inBufferList->mBuffers[handle->iStream[1]+i].mData;
- streamChannels = inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels;
-
- inJump = 0;
- // Account for possible channel offset in first stream
- if ( i == 0 && stream_.channelOffset[1] > 0 ) {
- streamChannels -= stream_.channelOffset[1];
- inJump = stream_.channelOffset[1];
- in += inJump;
- }
-
- // Account for possible unread channels at end of the last stream
- if ( streamChannels > channelsLeft ) {
- inJump = streamChannels - channelsLeft;
- streamChannels = channelsLeft;
- }
-
- // Determine output buffer offsets and skips
- if ( outInterleaved ) {
- outJump = outChannels;
- out += outChannels - channelsLeft;
- }
- else {
- outJump = 1;
- out += (outChannels - channelsLeft) * outOffset;
- }
-
- for ( unsigned int i=0; i<stream_.bufferSize; i++ ) {
- for ( unsigned int j=0; j<streamChannels; j++ ) {
- out[j*outOffset] = *in++;
- }
- out += outJump;
- in += inJump;
- }
- channelsLeft -= streamChannels;
- }
- }
-
- if ( stream_.doConvertBuffer[1] ) { // convert from our internal "device" buffer
- convertBuffer( stream_.userBuffer[1],
- stream_.deviceBuffer,
- stream_.convertInfo[1] );
- }
- }
- }
-
- unlock:
- //MUTEX_UNLOCK( &stream_.mutex );
-
- // Make sure to only tick duplex stream time once if using two devices
- if ( stream_.mode != DUPLEX || (stream_.mode == DUPLEX && handle->id[0] != handle->id[1] && deviceId == handle->id[0] ) )
- RtApi::tickStreamTime();
-
- return SUCCESS;
-}
-
-const char* RtApiCore :: getErrorCode( OSStatus code )
-{
- switch( code ) {
-
- case kAudioHardwareNotRunningError:
- return "kAudioHardwareNotRunningError";
-
- case kAudioHardwareUnspecifiedError:
- return "kAudioHardwareUnspecifiedError";
-
- case kAudioHardwareUnknownPropertyError:
- return "kAudioHardwareUnknownPropertyError";
-
- case kAudioHardwareBadPropertySizeError:
- return "kAudioHardwareBadPropertySizeError";
-
- case kAudioHardwareIllegalOperationError:
- return "kAudioHardwareIllegalOperationError";
-
- case kAudioHardwareBadObjectError:
- return "kAudioHardwareBadObjectError";
-
- case kAudioHardwareBadDeviceError:
- return "kAudioHardwareBadDeviceError";
-
- case kAudioHardwareBadStreamError:
- return "kAudioHardwareBadStreamError";
-
- case kAudioHardwareUnsupportedOperationError:
- return "kAudioHardwareUnsupportedOperationError";
-
- case kAudioDeviceUnsupportedFormatError:
- return "kAudioDeviceUnsupportedFormatError";
-
- case kAudioDevicePermissionsError:
- return "kAudioDevicePermissionsError";
-
- default:
- return "CoreAudio unknown error";
- }
-}
-
- //******************** End of __MACOSX_CORE__ *********************//
-#endif
-
-#if defined(__UNIX_JACK__)
-
-// JACK is a low-latency audio server, originally written for the
-// GNU/Linux operating system and now also ported to OS-X. It can
-// connect a number of different applications to an audio device, as
-// well as allowing them to share audio between themselves.
-//
-// When using JACK with RtAudio, "devices" refer to JACK clients that
-// have ports connected to the server. The JACK server is typically
-// started in a terminal as follows:
-//
-// .jackd -d alsa -d hw:0
-//
-// or through an interface program such as qjackctl. Many of the
-// parameters normally set for a stream are fixed by the JACK server
-// and can be specified when the JACK server is started. In
-// particular,
-//
-// .jackd -d alsa -d hw:0 -r 44100 -p 512 -n 4
-//
-// specifies a sample rate of 44100 Hz, a buffer size of 512 sample
-// frames, and number of buffers = 4. Once the server is running, it
-// is not possible to override these values. If the values are not
-// specified in the command-line, the JACK server uses default values.
-//
-// The JACK server does not have to be running when an instance of
-// RtApiJack is created, though the function getDeviceCount() will
-// report 0 devices found until JACK has been started. When no
-// devices are available (i.e., the JACK server is not running), a
-// stream cannot be opened.
-
-#include <jack/jack.h>
-#include <unistd.h>
-#include <cstdio>
-
-// A structure to hold various information related to the Jack API
-// implementation.
-struct JackHandle {
- jack_client_t *client;
- jack_port_t **ports[2];
- std::string deviceName[2];
- bool xrun[2];
- pthread_cond_t condition;
- int drainCounter; // Tracks callback counts when draining
- bool internalDrain; // Indicates if stop is initiated from callback or not.
-
- JackHandle()
- :client(0), drainCounter(0), internalDrain(false) { ports[0] = 0; ports[1] = 0; xrun[0] = false; xrun[1] = false; }
-};
-
-#if defined(__UNIX_JACK__)
-void* RtAudio :: HACK__getJackClient()
-{
- return static_cast<JackHandle*>(rtapi_->stream_.apiHandle)->client;
-}
-#endif
-
-#if !defined(__RTAUDIO_DEBUG__)
-static void jackSilentError( const char * ) {};
-#endif
-
-RtApiJack :: RtApiJack()
- :shouldAutoconnect_(true) {
- // Nothing to do here.
-#if !defined(__RTAUDIO_DEBUG__)
- // Turn off Jack's internal error reporting.
- jack_set_error_function( &jackSilentError );
-#endif
-}
-
-RtApiJack :: ~RtApiJack()
-{
- if ( stream_.state != STREAM_CLOSED ) closeStream();
-}
-
-unsigned int RtApiJack :: getDeviceCount( void )
-{
- // See if we can become a jack client.
- jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption;
- jack_status_t *status = NULL;
- jack_client_t *client = jack_client_open( "RtApiJackCount", options, status );
- if ( client == 0 ) return 0;
-
- const char **ports;
- std::string port, previousPort;
- unsigned int nChannels = 0, nDevices = 0;
- ports = jack_get_ports( client, NULL, JACK_DEFAULT_AUDIO_TYPE, 0 );
- if ( ports ) {
- // Parse the port names up to the first colon (:).
- size_t iColon = 0;
- do {
- port = (char *) ports[ nChannels ];
- iColon = port.find(":");
- if ( iColon != std::string::npos ) {
- port = port.substr( 0, iColon + 1 );
- if ( port != previousPort ) {
- nDevices++;
- previousPort = port;
- }
- }
- } while ( ports[++nChannels] );
- free( ports );
- }
-
- jack_client_close( client );
- return nDevices;
-}
-
-RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
-{
- RtAudio::DeviceInfo info;
- info.probed = false;
-
- jack_options_t options = (jack_options_t) ( JackNoStartServer ); //JackNullOption
- jack_status_t *status = NULL;
- jack_client_t *client = jack_client_open( "RtApiJackInfo", options, status );
- if ( client == 0 ) {
- errorText_ = "RtApiJack::getDeviceInfo: Jack server not found or connection error!";
- error( RtAudioError::WARNING );
- return info;
- }
-
- const char **ports;
- std::string port, previousPort;
- unsigned int nPorts = 0, nDevices = 0;
- ports = jack_get_ports( client, NULL, JACK_DEFAULT_AUDIO_TYPE, 0 );
- if ( ports ) {
- // Parse the port names up to the first colon (:).
- size_t iColon = 0;
- do {
- port = (char *) ports[ nPorts ];
- iColon = port.find(":");
- if ( iColon != std::string::npos ) {
- port = port.substr( 0, iColon );
- if ( port != previousPort ) {
- if ( nDevices == device ) info.name = port;
- nDevices++;
- previousPort = port;
- }
- }
- } while ( ports[++nPorts] );
- free( ports );
- }
-
- if ( device >= nDevices ) {
- jack_client_close( client );
- errorText_ = "RtApiJack::getDeviceInfo: device ID is invalid!";
- error( RtAudioError::INVALID_USE );
- return info;
- }
-
- // Get the current jack server sample rate.
- info.sampleRates.clear();
-
- info.preferredSampleRate = jack_get_sample_rate( client );
- info.sampleRates.push_back( info.preferredSampleRate );
-
- // Count the available ports containing the client name as device
- // channels. Jack "input ports" equal RtAudio output channels.
- unsigned int nChannels = 0;
- ports = jack_get_ports( client, info.name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput );
- if ( ports ) {
- while ( ports[ nChannels ] ) nChannels++;
- free( ports );
- info.outputChannels = nChannels;
- }
-
- // Jack "output ports" equal RtAudio input channels.
- nChannels = 0;
- ports = jack_get_ports( client, info.name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput );
- if ( ports ) {
- while ( ports[ nChannels ] ) nChannels++;
- free( ports );
- info.inputChannels = nChannels;
- }
-
- if ( info.outputChannels == 0 && info.inputChannels == 0 ) {
- jack_client_close(client);
- errorText_ = "RtApiJack::getDeviceInfo: error determining Jack input/output channels!";
- error( RtAudioError::WARNING );
- return info;
- }
-
- // If device opens for both playback and capture, we determine the channels.
- if ( info.outputChannels > 0 && info.inputChannels > 0 )
- info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
-
- // Jack always uses 32-bit floats.
- info.nativeFormats = RTAUDIO_FLOAT32;
-
- // Jack doesn't provide default devices so we'll use the first available one.
- if ( device == 0 && info.outputChannels > 0 )
- info.isDefaultOutput = true;
- if ( device == 0 && info.inputChannels > 0 )
- info.isDefaultInput = true;
-
- jack_client_close(client);
- info.probed = true;
- return info;
-}
-
-static int jackCallbackHandler( jack_nframes_t nframes, void *infoPointer )
-{
- CallbackInfo *info = (CallbackInfo *) infoPointer;
-
- RtApiJack *object = (RtApiJack *) info->object;
- if ( object->callbackEvent( (unsigned long) nframes ) == false ) return 1;
-
- return 0;
-}
-
-// This function will be called by a spawned thread when the Jack
-// server signals that it is shutting down. It is necessary to handle
-// it this way because the jackShutdown() function must return before
-// the jack_deactivate() function (in closeStream()) will return.
-static void *jackCloseStream( void *ptr )
-{
- CallbackInfo *info = (CallbackInfo *) ptr;
- RtApiJack *object = (RtApiJack *) info->object;
-
- object->closeStream();
-
- pthread_exit( NULL );
-}
-static void jackShutdown( void *infoPointer )
-{
- CallbackInfo *info = (CallbackInfo *) infoPointer;
- RtApiJack *object = (RtApiJack *) info->object;
-
- // Check current stream state. If stopped, then we'll assume this
- // was called as a result of a call to RtApiJack::stopStream (the
- // deactivation of a client handle causes this function to be called).
- // If not, we'll assume the Jack server is shutting down or some
- // other problem occurred and we should close the stream.
- if ( object->isStreamRunning() == false ) return;
-
- ThreadHandle threadId;
- pthread_create( &threadId, NULL, jackCloseStream, info );
- std::cerr << "\nRtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!\n" << std::endl;
-}
-
-static int jackXrun( void *infoPointer )
-{
- JackHandle *handle = *((JackHandle **) infoPointer);
-
- if ( handle->ports[0] ) handle->xrun[0] = true;
- if ( handle->ports[1] ) handle->xrun[1] = true;
-
- return 0;
-}
-
-bool RtApiJack :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
- RtAudioFormat format, unsigned int *bufferSize,
- RtAudio::StreamOptions *options )
-{
- JackHandle *handle = (JackHandle *) stream_.apiHandle;
-
- // Look for jack server and try to become a client (only do once per stream).
- jack_client_t *client = 0;
- if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) {
- jack_options_t jackoptions = (jack_options_t) ( JackNoStartServer ); //JackNullOption;
- jack_status_t *status = NULL;
- if ( options && !options->streamName.empty() )
- client = jack_client_open( options->streamName.c_str(), jackoptions, status );
- else
- client = jack_client_open( "RtApiJack", jackoptions, status );
- if ( client == 0 ) {
- errorText_ = "RtApiJack::probeDeviceOpen: Jack server not found or connection error!";
- error( RtAudioError::WARNING );
- return FAILURE;
- }
- }
- else {
- // The handle must have been created on an earlier pass.
- client = handle->client;
- }
-
- const char **ports;
- std::string port, previousPort, deviceName;
- unsigned int nPorts = 0, nDevices = 0;
- ports = jack_get_ports( client, NULL, JACK_DEFAULT_AUDIO_TYPE, 0 );
- if ( ports ) {
- // Parse the port names up to the first colon (:).
- size_t iColon = 0;
- do {
- port = (char *) ports[ nPorts ];
- iColon = port.find(":");
- if ( iColon != std::string::npos ) {
- port = port.substr( 0, iColon );
- if ( port != previousPort ) {
- if ( nDevices == device ) deviceName = port;
- nDevices++;
- previousPort = port;
- }
- }
- } while ( ports[++nPorts] );
- free( ports );
- }
-
- if ( device >= nDevices ) {
- errorText_ = "RtApiJack::probeDeviceOpen: device ID is invalid!";
- return FAILURE;
- }
-
- unsigned long flag = JackPortIsInput;
- if ( mode == INPUT ) flag = JackPortIsOutput;
-
- if ( ! (options && (options->flags & RTAUDIO_JACK_DONT_CONNECT)) ) {
- // Count the available ports containing the client name as device
- // channels. Jack "input ports" equal RtAudio output channels.
- unsigned int nChannels = 0;
- ports = jack_get_ports( client, deviceName.c_str(), JACK_DEFAULT_AUDIO_TYPE, flag );
- if ( ports ) {
- while ( ports[ nChannels ] ) nChannels++;
- free( ports );
- }
- // Compare the jack ports for specified client to the requested number of channels.
- if ( nChannels < (channels + firstChannel) ) {
- errorStream_ << "RtApiJack::probeDeviceOpen: requested number of channels (" << channels << ") + offset (" << firstChannel << ") not found for specified device (" << device << ":" << deviceName << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- }
-
- // Check the jack server sample rate.
- unsigned int jackRate = jack_get_sample_rate( client );
- if ( sampleRate != jackRate ) {
- jack_client_close( client );
- errorStream_ << "RtApiJack::probeDeviceOpen: the requested sample rate (" << sampleRate << ") is different than the JACK server rate (" << jackRate << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- stream_.sampleRate = jackRate;
-
- // Get the latency of the JACK port.
- ports = jack_get_ports( client, deviceName.c_str(), JACK_DEFAULT_AUDIO_TYPE, flag );
- if ( ports[ firstChannel ] ) {
- // Added by Ge Wang
- jack_latency_callback_mode_t cbmode = (mode == INPUT ? JackCaptureLatency : JackPlaybackLatency);
- // the range (usually the min and max are equal)
- jack_latency_range_t latrange; latrange.min = latrange.max = 0;
- // get the latency range
- jack_port_get_latency_range( jack_port_by_name( client, ports[firstChannel] ), cbmode, &latrange );
- // be optimistic, use the min!
- stream_.latency[mode] = latrange.min;
- //stream_.latency[mode] = jack_port_get_latency( jack_port_by_name( client, ports[ firstChannel ] ) );
- }
- free( ports );
-
- // The jack server always uses 32-bit floating-point data.
- stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
- stream_.userFormat = format;
-
- if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
- else stream_.userInterleaved = true;
-
- // Jack always uses non-interleaved buffers.
- stream_.deviceInterleaved[mode] = false;
-
- // Jack always provides host byte-ordered data.
- stream_.doByteSwap[mode] = false;
-
- // Get the buffer size. The buffer size and number of buffers
- // (periods) is set when the jack server is started.
- stream_.bufferSize = (int) jack_get_buffer_size( client );
- *bufferSize = stream_.bufferSize;
-
- stream_.nDeviceChannels[mode] = channels;
- stream_.nUserChannels[mode] = channels;
-
- // Set flags for buffer conversion.
- stream_.doConvertBuffer[mode] = false;
- if ( stream_.userFormat != stream_.deviceFormat[mode] )
- stream_.doConvertBuffer[mode] = true;
- if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
- stream_.nUserChannels[mode] > 1 )
- stream_.doConvertBuffer[mode] = true;
-
- // Allocate our JackHandle structure for the stream.
- if ( handle == 0 ) {
- try {
- handle = new JackHandle;
- }
- catch ( std::bad_alloc& ) {
- errorText_ = "RtApiJack::probeDeviceOpen: error allocating JackHandle memory.";
- goto error;
- }
-
- if ( pthread_cond_init(&handle->condition, NULL) ) {
- errorText_ = "RtApiJack::probeDeviceOpen: error initializing pthread condition variable.";
- goto error;
- }
- stream_.apiHandle = (void *) handle;
- handle->client = client;
- }
- handle->deviceName[mode] = deviceName;
-
- // Allocate necessary internal buffers.
- unsigned long bufferBytes;
- bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
- stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
- if ( stream_.userBuffer[mode] == NULL ) {
- errorText_ = "RtApiJack::probeDeviceOpen: error allocating user buffer memory.";
- goto error;
- }
-
- if ( stream_.doConvertBuffer[mode] ) {
-
- bool makeBuffer = true;
- if ( mode == OUTPUT )
- bufferBytes = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
- else { // mode == INPUT
- bufferBytes = stream_.nDeviceChannels[1] * formatBytes( stream_.deviceFormat[1] );
- if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
- unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes(stream_.deviceFormat[0]);
- if ( bufferBytes < bytesOut ) makeBuffer = false;
- }
- }
-
- if ( makeBuffer ) {
- bufferBytes *= *bufferSize;
- if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
- stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
- if ( stream_.deviceBuffer == NULL ) {
- errorText_ = "RtApiJack::probeDeviceOpen: error allocating device buffer memory.";
- goto error;
- }
- }
- }
-
- // Allocate memory for the Jack ports (channels) identifiers.
- handle->ports[mode] = (jack_port_t **) malloc ( sizeof (jack_port_t *) * channels );
- if ( handle->ports[mode] == NULL ) {
- errorText_ = "RtApiJack::probeDeviceOpen: error allocating port memory.";
- goto error;
- }
-
- stream_.device[mode] = device;
- stream_.channelOffset[mode] = firstChannel;
- stream_.state = STREAM_STOPPED;
- stream_.callbackInfo.object = (void *) this;
-
- if ( stream_.mode == OUTPUT && mode == INPUT )
- // We had already set up the stream for output.
- stream_.mode = DUPLEX;
- else {
- stream_.mode = mode;
- jack_set_process_callback( handle->client, jackCallbackHandler, (void *) &stream_.callbackInfo );
- jack_set_xrun_callback( handle->client, jackXrun, (void *) &stream_.apiHandle );
- jack_on_shutdown( handle->client, jackShutdown, (void *) &stream_.callbackInfo );
- }
-
- // Register our ports.
- char label[64];
- if ( mode == OUTPUT ) {
- for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
- snprintf( label, 64, "outport %d", i );
- handle->ports[0][i] = jack_port_register( handle->client, (const char *)label,
- JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
- }
- }
- else {
- for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
- snprintf( label, 64, "inport %d", i );
- handle->ports[1][i] = jack_port_register( handle->client, (const char *)label,
- JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 );
- }
- }
-
- // Setup the buffer conversion information structure. We don't use
- // buffers to do channel offsets, so we override that parameter
- // here.
- if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );
-
- if ( options && options->flags & RTAUDIO_JACK_DONT_CONNECT ) shouldAutoconnect_ = false;
-
- return SUCCESS;
-
- error:
- if ( handle ) {
- pthread_cond_destroy( &handle->condition );
- jack_client_close( handle->client );
-
- if ( handle->ports[0] ) free( handle->ports[0] );
- if ( handle->ports[1] ) free( handle->ports[1] );
-
- delete handle;
- stream_.apiHandle = 0;
- }
-
- for ( int i=0; i<2; i++ ) {
- if ( stream_.userBuffer[i] ) {
- free( stream_.userBuffer[i] );
- stream_.userBuffer[i] = 0;
- }
- }
-
- if ( stream_.deviceBuffer ) {
- free( stream_.deviceBuffer );
- stream_.deviceBuffer = 0;
- }
-
- return FAILURE;
-}
-
-void RtApiJack :: closeStream( void )
-{
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiJack::closeStream(): no open stream to close!";
- error( RtAudioError::WARNING );
- return;
- }
-
- JackHandle *handle = (JackHandle *) stream_.apiHandle;
- if ( handle ) {
-
- if ( stream_.state == STREAM_RUNNING )
- jack_deactivate( handle->client );
-
- jack_client_close( handle->client );
- }
-
- if ( handle ) {
- if ( handle->ports[0] ) free( handle->ports[0] );
- if ( handle->ports[1] ) free( handle->ports[1] );
- pthread_cond_destroy( &handle->condition );
- delete handle;
- stream_.apiHandle = 0;
- }
-
- for ( int i=0; i<2; i++ ) {
- if ( stream_.userBuffer[i] ) {
- free( stream_.userBuffer[i] );
- stream_.userBuffer[i] = 0;
- }
- }
-
- if ( stream_.deviceBuffer ) {
- free( stream_.deviceBuffer );
- stream_.deviceBuffer = 0;
- }
-
- stream_.mode = UNINITIALIZED;
- stream_.state = STREAM_CLOSED;
-}
-
-void RtApiJack :: startStream( void )
-{
- verifyStream();
- if ( stream_.state == STREAM_RUNNING ) {
- errorText_ = "RtApiJack::startStream(): the stream is already running!";
- error( RtAudioError::WARNING );
- return;
- }
-
- #if defined( HAVE_GETTIMEOFDAY )
- gettimeofday( &stream_.lastTickTimestamp, NULL );
- #endif
-
- JackHandle *handle = (JackHandle *) stream_.apiHandle;
- int result = jack_activate( handle->client );
- if ( result ) {
- errorText_ = "RtApiJack::startStream(): unable to activate JACK client!";
- goto unlock;
- }
-
- const char **ports;
-
- // Get the list of available ports.
- if ( shouldAutoconnect_ && (stream_.mode == OUTPUT || stream_.mode == DUPLEX) ) {
- result = 1;
- ports = jack_get_ports( handle->client, handle->deviceName[0].c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput);
- if ( ports == NULL) {
- errorText_ = "RtApiJack::startStream(): error determining available JACK input ports!";
- goto unlock;
- }
-
- // Now make the port connections. Since RtAudio wasn't designed to
- // allow the user to select particular channels of a device, we'll
- // just open the first "nChannels" ports with offset.
- for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
- result = 1;
- if ( ports[ stream_.channelOffset[0] + i ] )
- result = jack_connect( handle->client, jack_port_name( handle->ports[0][i] ), ports[ stream_.channelOffset[0] + i ] );
- if ( result ) {
- free( ports );
- errorText_ = "RtApiJack::startStream(): error connecting output ports!";
- goto unlock;
- }
- }
- free(ports);
- }
-
- if ( shouldAutoconnect_ && (stream_.mode == INPUT || stream_.mode == DUPLEX) ) {
- result = 1;
- ports = jack_get_ports( handle->client, handle->deviceName[1].c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput );
- if ( ports == NULL) {
- errorText_ = "RtApiJack::startStream(): error determining available JACK output ports!";
- goto unlock;
- }
-
- // Now make the port connections. See note above.
- for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
- result = 1;
- if ( ports[ stream_.channelOffset[1] + i ] )
- result = jack_connect( handle->client, ports[ stream_.channelOffset[1] + i ], jack_port_name( handle->ports[1][i] ) );
- if ( result ) {
- free( ports );
- errorText_ = "RtApiJack::startStream(): error connecting input ports!";
- goto unlock;
- }
- }
- free(ports);
- }
-
- handle->drainCounter = 0;
- handle->internalDrain = false;
- stream_.state = STREAM_RUNNING;
-
- unlock:
- if ( result == 0 ) return;
- error( RtAudioError::SYSTEM_ERROR );
-}
-
-void RtApiJack :: stopStream( void )
-{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiJack::stopStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
- }
-
- JackHandle *handle = (JackHandle *) stream_.apiHandle;
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
-
- if ( handle->drainCounter == 0 ) {
- handle->drainCounter = 2;
- pthread_cond_wait( &handle->condition, &stream_.mutex ); // block until signaled
- }
- }
-
- jack_deactivate( handle->client );
- stream_.state = STREAM_STOPPED;
-}
-
-void RtApiJack :: abortStream( void )
-{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiJack::abortStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
- }
-
- JackHandle *handle = (JackHandle *) stream_.apiHandle;
- handle->drainCounter = 2;
-
- stopStream();
-}
-
-// This function will be called by a spawned thread when the user
-// callback function signals that the stream should be stopped or
-// aborted. It is necessary to handle it this way because the
-// callbackEvent() function must return before the jack_deactivate()
-// function will return.
-static void *jackStopStream( void *ptr )
-{
- CallbackInfo *info = (CallbackInfo *) ptr;
- RtApiJack *object = (RtApiJack *) info->object;
-
- object->stopStream();
- pthread_exit( NULL );
-}
-
-bool RtApiJack :: callbackEvent( unsigned long nframes )
-{
- if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!";
- error( RtAudioError::WARNING );
- return FAILURE;
- }
- if ( stream_.bufferSize != nframes ) {
- errorText_ = "RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!";
- error( RtAudioError::WARNING );
- return FAILURE;
- }
-
- CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
- JackHandle *handle = (JackHandle *) stream_.apiHandle;
-
- // Check if we were draining the stream and signal is finished.
- if ( handle->drainCounter > 3 ) {
- ThreadHandle threadId;
-
- stream_.state = STREAM_STOPPING;
- if ( handle->internalDrain == true )
- pthread_create( &threadId, NULL, jackStopStream, info );
- else
- pthread_cond_signal( &handle->condition );
- return SUCCESS;
- }
-
- // Invoke user callback first, to get fresh output data.
- if ( handle->drainCounter == 0 ) {
- RtAudioCallback callback = (RtAudioCallback) info->callback;
- double streamTime = getStreamTime();
- RtAudioStreamStatus status = 0;
- if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
- status |= RTAUDIO_OUTPUT_UNDERFLOW;
- handle->xrun[0] = false;
- }
- if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
- status |= RTAUDIO_INPUT_OVERFLOW;
- handle->xrun[1] = false;
- }
- int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
- stream_.bufferSize, streamTime, status, info->userData );
- if ( cbReturnValue == 2 ) {
- stream_.state = STREAM_STOPPING;
- handle->drainCounter = 2;
- ThreadHandle id;
- pthread_create( &id, NULL, jackStopStream, info );
- return SUCCESS;
- }
- else if ( cbReturnValue == 1 ) {
- handle->drainCounter = 1;
- handle->internalDrain = true;
- }
- }
-
- jack_default_audio_sample_t *jackbuffer;
- unsigned long bufferBytes = nframes * sizeof( jack_default_audio_sample_t );
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
-
- if ( handle->drainCounter > 1 ) { // write zeros to the output stream
-
- for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
- jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
- memset( jackbuffer, 0, bufferBytes );
- }
-
- }
- else if ( stream_.doConvertBuffer[0] ) {
-
- convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
-
- for ( unsigned int i=0; i<stream_.nDeviceChannels[0]; i++ ) {
- jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
- memcpy( jackbuffer, &stream_.deviceBuffer[i*bufferBytes], bufferBytes );
- }
- }
- else { // no buffer conversion
- for ( unsigned int i=0; i<stream_.nUserChannels[0]; i++ ) {
- jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[0][i], (jack_nframes_t) nframes );
- memcpy( jackbuffer, &stream_.userBuffer[0][i*bufferBytes], bufferBytes );
- }
- }
- }
-
- // Don't bother draining input
- if ( handle->drainCounter ) {
- handle->drainCounter++;
- goto unlock;
- }
-
- if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
-
- if ( stream_.doConvertBuffer[1] ) {
- for ( unsigned int i=0; i<stream_.nDeviceChannels[1]; i++ ) {
- jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );
- memcpy( &stream_.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes );
- }
- convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
- }
- else { // no buffer conversion
- for ( unsigned int i=0; i<stream_.nUserChannels[1]; i++ ) {
- jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer( handle->ports[1][i], (jack_nframes_t) nframes );
- memcpy( &stream_.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes );
- }
- }
- }
-
- unlock:
- RtApi::tickStreamTime();
- return SUCCESS;
-}
- //******************** End of __UNIX_JACK__ *********************//
-#endif
-
-#if defined(__WINDOWS_ASIO__) // ASIO API on Windows
-
-// The ASIO API is designed around a callback scheme, so this
-// implementation is similar to that used for OS-X CoreAudio and Linux
-// Jack. The primary constraint with ASIO is that it only allows
-// access to a single driver at a time. Thus, it is not possible to
-// have more than one simultaneous RtAudio stream.
-//
-// This implementation also requires a number of external ASIO files
-// and a few global variables. The ASIO callback scheme does not
-// allow for the passing of user data, so we must create a global
-// pointer to our callbackInfo structure.
-//
-// On unix systems, we make use of a pthread condition variable.
-// Since there is no equivalent in Windows, I hacked something based
-// on information found in
-// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html.
-
-#include "asiosys.h"
-#include "asio.h"
-#include "iasiothiscallresolver.h"
-#include "asiodrivers.h"
-#include <cmath>
-
-static AsioDrivers drivers;
-static ASIOCallbacks asioCallbacks;
-static ASIODriverInfo driverInfo;
-static CallbackInfo *asioCallbackInfo;
-static bool asioXRun;
-
-struct AsioHandle {
- int drainCounter; // Tracks callback counts when draining
- bool internalDrain; // Indicates if stop is initiated from callback or not.
- ASIOBufferInfo *bufferInfos;
- HANDLE condition;
-
- AsioHandle()
- :drainCounter(0), internalDrain(false), bufferInfos(0) {}
-};
-
-// Function declarations (definitions at end of section)
-static const char* getAsioErrorString( ASIOError result );
-static void sampleRateChanged( ASIOSampleRate sRate );
-static long asioMessages( long selector, long value, void* message, double* opt );
-
-RtApiAsio :: RtApiAsio()
-{
- // ASIO cannot run on a multi-threaded appartment. You can call
- // CoInitialize beforehand, but it must be for appartment threading
- // (in which case, CoInitilialize will return S_FALSE here).
- coInitialized_ = false;
- HRESULT hr = CoInitialize( NULL );
- if ( FAILED(hr) ) {
- errorText_ = "RtApiAsio::ASIO requires a single-threaded appartment. Call CoInitializeEx(0,COINIT_APARTMENTTHREADED)";
- error( RtAudioError::WARNING );
- }
- coInitialized_ = true;
-
- drivers.removeCurrentDriver();
- driverInfo.asioVersion = 2;
-
- // See note in DirectSound implementation about GetDesktopWindow().
- driverInfo.sysRef = GetForegroundWindow();
-}
-
-RtApiAsio :: ~RtApiAsio()
-{
- if ( stream_.state != STREAM_CLOSED ) closeStream();
- if ( coInitialized_ ) CoUninitialize();
-}
-
-unsigned int RtApiAsio :: getDeviceCount( void )
-{
- return (unsigned int) drivers.asioGetNumDev();
-}
-
-RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
-{
- RtAudio::DeviceInfo info;
- info.probed = false;
-
- // Get device ID
- unsigned int nDevices = getDeviceCount();
- if ( nDevices == 0 ) {
- errorText_ = "RtApiAsio::getDeviceInfo: no devices found!";
- error( RtAudioError::INVALID_USE );
- return info;
- }
-
- if ( device >= nDevices ) {
- errorText_ = "RtApiAsio::getDeviceInfo: device ID is invalid!";
- error( RtAudioError::INVALID_USE );
- return info;
- }
-
- // If a stream is already open, we cannot probe other devices. Thus, use the saved results.
- if ( stream_.state != STREAM_CLOSED ) {
- if ( device >= devices_.size() ) {
- errorText_ = "RtApiAsio::getDeviceInfo: device ID was not present before stream was opened.";
- error( RtAudioError::WARNING );
- return info;
- }
- return devices_[ device ];
- }
-
- char driverName[32];
- ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );
- if ( result != ASE_OK ) {
- errorStream_ << "RtApiAsio::getDeviceInfo: unable to get driver name (" << getAsioErrorString( result ) << ").";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- info.name = driverName;
-
- if ( !drivers.loadDriver( driverName ) ) {
- errorStream_ << "RtApiAsio::getDeviceInfo: unable to load driver (" << driverName << ").";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- result = ASIOInit( &driverInfo );
- if ( result != ASE_OK ) {
- errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- // Determine the device channel information.
- long inputChannels, outputChannels;
- result = ASIOGetChannels( &inputChannels, &outputChannels );
- if ( result != ASE_OK ) {
- drivers.removeCurrentDriver();
- errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- info.outputChannels = outputChannels;
- info.inputChannels = inputChannels;
- if ( info.outputChannels > 0 && info.inputChannels > 0 )
- info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
-
- // Determine the supported sample rates.
- info.sampleRates.clear();
- for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
- result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );
- if ( result == ASE_OK ) {
- info.sampleRates.push_back( SAMPLE_RATES[i] );
-
- if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )
- info.preferredSampleRate = SAMPLE_RATES[i];
- }
- }
-
- // Determine supported data types ... just check first channel and assume rest are the same.
- ASIOChannelInfo channelInfo;
- channelInfo.channel = 0;
- channelInfo.isInput = true;
- if ( info.inputChannels <= 0 ) channelInfo.isInput = false;
- result = ASIOGetChannelInfo( &channelInfo );
- if ( result != ASE_OK ) {
- drivers.removeCurrentDriver();
- errorStream_ << "RtApiAsio::getDeviceInfo: error (" << getAsioErrorString( result ) << ") getting driver channel info (" << driverName << ").";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- info.nativeFormats = 0;
- if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB )
- info.nativeFormats |= RTAUDIO_SINT16;
- else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB )
- info.nativeFormats |= RTAUDIO_SINT32;
- else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB )
- info.nativeFormats |= RTAUDIO_FLOAT32;
- else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB )
- info.nativeFormats |= RTAUDIO_FLOAT64;
- else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB )
- info.nativeFormats |= RTAUDIO_SINT24;
-
- if ( info.outputChannels > 0 )
- if ( getDefaultOutputDevice() == device ) info.isDefaultOutput = true;
- if ( info.inputChannels > 0 )
- if ( getDefaultInputDevice() == device ) info.isDefaultInput = true;
-
- info.probed = true;
- drivers.removeCurrentDriver();
- return info;
-}
-
-static void bufferSwitch( long index, ASIOBool /*processNow*/ )
-{
- RtApiAsio *object = (RtApiAsio *) asioCallbackInfo->object;
- object->callbackEvent( index );
-}
-
-void RtApiAsio :: saveDeviceInfo( void )
-{
- devices_.clear();
-
- unsigned int nDevices = getDeviceCount();
- devices_.resize( nDevices );
- for ( unsigned int i=0; i<nDevices; i++ )
- devices_[i] = getDeviceInfo( i );
-}
-
-bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
- RtAudioFormat format, unsigned int *bufferSize,
- RtAudio::StreamOptions *options )
-{////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- bool isDuplexInput = mode == INPUT && stream_.mode == OUTPUT;
-
- // For ASIO, a duplex stream MUST use the same driver.
- if ( isDuplexInput && stream_.device[0] != device ) {
- errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!";
- return FAILURE;
- }
-
- char driverName[32];
- ASIOError result = drivers.asioGetDriverName( (int) device, driverName, 32 );
- if ( result != ASE_OK ) {
- errorStream_ << "RtApiAsio::probeDeviceOpen: unable to get driver name (" << getAsioErrorString( result ) << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Only load the driver once for duplex stream.
- if ( !isDuplexInput ) {
- // The getDeviceInfo() function will not work when a stream is open
- // because ASIO does not allow multiple devices to run at the same
- // time. Thus, we'll probe the system before opening a stream and
- // save the results for use by getDeviceInfo().
- this->saveDeviceInfo();
-
- if ( !drivers.loadDriver( driverName ) ) {
- errorStream_ << "RtApiAsio::probeDeviceOpen: unable to load driver (" << driverName << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- result = ASIOInit( &driverInfo );
- if ( result != ASE_OK ) {
- errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") initializing driver (" << driverName << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- }
-
- // keep them before any "goto error", they are used for error cleanup + goto device boundary checks
- bool buffersAllocated = false;
- AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
- unsigned int nChannels;
-
-
- // Check the device channel count.
- long inputChannels, outputChannels;
- result = ASIOGetChannels( &inputChannels, &outputChannels );
- if ( result != ASE_OK ) {
- errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";
- errorText_ = errorStream_.str();
- goto error;
- }
-
- if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||
- ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {
- errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";
- errorText_ = errorStream_.str();
- goto error;
- }
- stream_.nDeviceChannels[mode] = channels;
- stream_.nUserChannels[mode] = channels;
- stream_.channelOffset[mode] = firstChannel;
-
- // Verify the sample rate is supported.
- result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );
- if ( result != ASE_OK ) {
- errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";
- errorText_ = errorStream_.str();
- goto error;
- }
-
- // Get the current sample rate
- ASIOSampleRate currentRate;
- result = ASIOGetSampleRate( ¤tRate );
- if ( result != ASE_OK ) {
- errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate.";
- errorText_ = errorStream_.str();
- goto error;
- }
-
- // Set the sample rate only if necessary
- if ( currentRate != sampleRate ) {
- result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );
- if ( result != ASE_OK ) {
- errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";
- errorText_ = errorStream_.str();
- goto error;
- }
- }
-
- // Determine the driver data type.
- ASIOChannelInfo channelInfo;
- channelInfo.channel = 0;
- if ( mode == OUTPUT ) channelInfo.isInput = false;
- else channelInfo.isInput = true;
- result = ASIOGetChannelInfo( &channelInfo );
- if ( result != ASE_OK ) {
- errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";
- errorText_ = errorStream_.str();
- goto error;
- }
-
- // Assuming WINDOWS host is always little-endian.
- stream_.doByteSwap[mode] = false;
- stream_.userFormat = format;
- stream_.deviceFormat[mode] = 0;
- if ( channelInfo.type == ASIOSTInt16MSB || channelInfo.type == ASIOSTInt16LSB ) {
- stream_.deviceFormat[mode] = RTAUDIO_SINT16;
- if ( channelInfo.type == ASIOSTInt16MSB ) stream_.doByteSwap[mode] = true;
- }
- else if ( channelInfo.type == ASIOSTInt32MSB || channelInfo.type == ASIOSTInt32LSB ) {
- stream_.deviceFormat[mode] = RTAUDIO_SINT32;
- if ( channelInfo.type == ASIOSTInt32MSB ) stream_.doByteSwap[mode] = true;
- }
- else if ( channelInfo.type == ASIOSTFloat32MSB || channelInfo.type == ASIOSTFloat32LSB ) {
- stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
- if ( channelInfo.type == ASIOSTFloat32MSB ) stream_.doByteSwap[mode] = true;
- }
- else if ( channelInfo.type == ASIOSTFloat64MSB || channelInfo.type == ASIOSTFloat64LSB ) {
- stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;
- if ( channelInfo.type == ASIOSTFloat64MSB ) stream_.doByteSwap[mode] = true;
- }
- else if ( channelInfo.type == ASIOSTInt24MSB || channelInfo.type == ASIOSTInt24LSB ) {
- stream_.deviceFormat[mode] = RTAUDIO_SINT24;
- if ( channelInfo.type == ASIOSTInt24MSB ) stream_.doByteSwap[mode] = true;
- }
-
- if ( stream_.deviceFormat[mode] == 0 ) {
- errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";
- errorText_ = errorStream_.str();
- goto error;
- }
-
- // Set the buffer size. For a duplex stream, this will end up
- // setting the buffer size based on the input constraints, which
- // should be ok.
- long minSize, maxSize, preferSize, granularity;
- result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );
- if ( result != ASE_OK ) {
- errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";
- errorText_ = errorStream_.str();
- goto error;
- }
-
- if ( isDuplexInput ) {
- // When this is the duplex input (output was opened before), then we have to use the same
- // buffersize as the output, because it might use the preferred buffer size, which most
- // likely wasn't passed as input to this. The buffer sizes have to be identically anyway,
- // So instead of throwing an error, make them equal. The caller uses the reference
- // to the "bufferSize" param as usual to set up processing buffers.
-
- *bufferSize = stream_.bufferSize;
-
- } else {
- if ( *bufferSize == 0 ) *bufferSize = preferSize;
- else if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
- else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
- else if ( granularity == -1 ) {
- // Make sure bufferSize is a power of two.
- int log2_of_min_size = 0;
- int log2_of_max_size = 0;
-
- for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {
- if ( minSize & ((long)1 << i) ) log2_of_min_size = i;
- if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;
- }
-
- long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );
- int min_delta_num = log2_of_min_size;
-
- for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {
- long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );
- if (current_delta < min_delta) {
- min_delta = current_delta;
- min_delta_num = i;
- }
- }
-
- *bufferSize = ( (unsigned int)1 << min_delta_num );
- if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
- else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
- }
- else if ( granularity != 0 ) {
- // Set to an even multiple of granularity, rounding up.
- *bufferSize = (*bufferSize + granularity-1) / granularity * granularity;
- }
- }
-
- /*
- // we don't use it anymore, see above!
- // Just left it here for the case...
- if ( isDuplexInput && stream_.bufferSize != *bufferSize ) {
- errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";
- goto error;
- }
- */
-
- stream_.bufferSize = *bufferSize;
- stream_.nBuffers = 2;
-
- if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
- else stream_.userInterleaved = true;
-
- // ASIO always uses non-interleaved buffers.
- stream_.deviceInterleaved[mode] = false;
-
- // Allocate, if necessary, our AsioHandle structure for the stream.
- if ( handle == 0 ) {
- try {
- handle = new AsioHandle;
- }
- catch ( std::bad_alloc& ) {
- errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";
- goto error;
- }
- handle->bufferInfos = 0;
-
- // Create a manual-reset event.
- handle->condition = CreateEvent( NULL, // no security
- TRUE, // manual-reset
- FALSE, // non-signaled initially
- NULL ); // unnamed
- stream_.apiHandle = (void *) handle;
- }
-
- // Create the ASIO internal buffers. Since RtAudio sets up input
- // and output separately, we'll have to dispose of previously
- // created output buffers for a duplex stream.
- if ( mode == INPUT && stream_.mode == OUTPUT ) {
- ASIODisposeBuffers();
- if ( handle->bufferInfos ) free( handle->bufferInfos );
- }
-
- // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.
- unsigned int i;
- nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
- handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );
- if ( handle->bufferInfos == NULL ) {
- errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ").";
- errorText_ = errorStream_.str();
- goto error;
- }
-
- ASIOBufferInfo *infos;
- infos = handle->bufferInfos;
- for ( i=0; i<stream_.nDeviceChannels[0]; i++, infos++ ) {
- infos->isInput = ASIOFalse;
- infos->channelNum = i + stream_.channelOffset[0];
- infos->buffers[0] = infos->buffers[1] = 0;
- }
- for ( i=0; i<stream_.nDeviceChannels[1]; i++, infos++ ) {
- infos->isInput = ASIOTrue;
- infos->channelNum = i + stream_.channelOffset[1];
- infos->buffers[0] = infos->buffers[1] = 0;
- }
-
- // prepare for callbacks
- stream_.sampleRate = sampleRate;
- stream_.device[mode] = device;
- stream_.mode = isDuplexInput ? DUPLEX : mode;
-
- // store this class instance before registering callbacks, that are going to use it
- asioCallbackInfo = &stream_.callbackInfo;
- stream_.callbackInfo.object = (void *) this;
-
- // Set up the ASIO callback structure and create the ASIO data buffers.
- asioCallbacks.bufferSwitch = &bufferSwitch;
- asioCallbacks.sampleRateDidChange = &sampleRateChanged;
- asioCallbacks.asioMessage = &asioMessages;
- asioCallbacks.bufferSwitchTimeInfo = NULL;
- result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );
- if ( result != ASE_OK ) {
- // Standard method failed. This can happen with strict/misbehaving drivers that return valid buffer size ranges
- // but only accept the preferred buffer size as parameter for ASIOCreateBuffers (e.g. Creative's ASIO driver).
- // In that case, let's be naïve and try that instead.
- *bufferSize = preferSize;
- stream_.bufferSize = *bufferSize;
- result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );
- }
-
- if ( result != ASE_OK ) {
- errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";
- errorText_ = errorStream_.str();
- goto error;
- }
- buffersAllocated = true;
- stream_.state = STREAM_STOPPED;
-
- // Set flags for buffer conversion.
- stream_.doConvertBuffer[mode] = false;
- if ( stream_.userFormat != stream_.deviceFormat[mode] )
- stream_.doConvertBuffer[mode] = true;
- if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
- stream_.nUserChannels[mode] > 1 )
- stream_.doConvertBuffer[mode] = true;
-
- // Allocate necessary internal buffers
- unsigned long bufferBytes;
- bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
- stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
- if ( stream_.userBuffer[mode] == NULL ) {
- errorText_ = "RtApiAsio::probeDeviceOpen: error allocating user buffer memory.";
- goto error;
- }
-
- if ( stream_.doConvertBuffer[mode] ) {
-
- bool makeBuffer = true;
- bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
- if ( isDuplexInput && stream_.deviceBuffer ) {
- unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
- if ( bufferBytes <= bytesOut ) makeBuffer = false;
- }
-
- if ( makeBuffer ) {
- bufferBytes *= *bufferSize;
- if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
- stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
- if ( stream_.deviceBuffer == NULL ) {
- errorText_ = "RtApiAsio::probeDeviceOpen: error allocating device buffer memory.";
- goto error;
- }
- }
- }
-
- // Determine device latencies
- long inputLatency, outputLatency;
- result = ASIOGetLatencies( &inputLatency, &outputLatency );
- if ( result != ASE_OK ) {
- errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency.";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING); // warn but don't fail
- }
- else {
- stream_.latency[0] = outputLatency;
- stream_.latency[1] = inputLatency;
- }
-
- // Setup the buffer conversion information structure. We don't use
- // buffers to do channel offsets, so we override that parameter
- // here.
- if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, 0 );
-
- return SUCCESS;
-
- error:
- if ( !isDuplexInput ) {
- // the cleanup for error in the duplex input, is done by RtApi::openStream
- // So we clean up for single channel only
-
- if ( buffersAllocated )
- ASIODisposeBuffers();
-
- drivers.removeCurrentDriver();
-
- if ( handle ) {
- CloseHandle( handle->condition );
- if ( handle->bufferInfos )
- free( handle->bufferInfos );
-
- delete handle;
- stream_.apiHandle = 0;
- }
-
-
- if ( stream_.userBuffer[mode] ) {
- free( stream_.userBuffer[mode] );
- stream_.userBuffer[mode] = 0;
- }
-
- if ( stream_.deviceBuffer ) {
- free( stream_.deviceBuffer );
- stream_.deviceBuffer = 0;
- }
- }
-
- return FAILURE;
-}////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-void RtApiAsio :: closeStream()
-{
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiAsio::closeStream(): no open stream to close!";
- error( RtAudioError::WARNING );
- return;
- }
-
- if ( stream_.state == STREAM_RUNNING ) {
- stream_.state = STREAM_STOPPED;
- ASIOStop();
- }
- ASIODisposeBuffers();
- drivers.removeCurrentDriver();
-
- AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
- if ( handle ) {
- CloseHandle( handle->condition );
- if ( handle->bufferInfos )
- free( handle->bufferInfos );
- delete handle;
- stream_.apiHandle = 0;
- }
-
- for ( int i=0; i<2; i++ ) {
- if ( stream_.userBuffer[i] ) {
- free( stream_.userBuffer[i] );
- stream_.userBuffer[i] = 0;
- }
- }
-
- if ( stream_.deviceBuffer ) {
- free( stream_.deviceBuffer );
- stream_.deviceBuffer = 0;
- }
-
- stream_.mode = UNINITIALIZED;
- stream_.state = STREAM_CLOSED;
-}
-
-bool stopThreadCalled = false;
-
-void RtApiAsio :: startStream()
-{
- verifyStream();
- if ( stream_.state == STREAM_RUNNING ) {
- errorText_ = "RtApiAsio::startStream(): the stream is already running!";
- error( RtAudioError::WARNING );
- return;
- }
-
- #if defined( HAVE_GETTIMEOFDAY )
- gettimeofday( &stream_.lastTickTimestamp, NULL );
- #endif
-
- AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
- ASIOError result = ASIOStart();
- if ( result != ASE_OK ) {
- errorStream_ << "RtApiAsio::startStream: error (" << getAsioErrorString( result ) << ") starting device.";
- errorText_ = errorStream_.str();
- goto unlock;
- }
-
- handle->drainCounter = 0;
- handle->internalDrain = false;
- ResetEvent( handle->condition );
- stream_.state = STREAM_RUNNING;
- asioXRun = false;
-
- unlock:
- stopThreadCalled = false;
-
- if ( result == ASE_OK ) return;
- error( RtAudioError::SYSTEM_ERROR );
-}
-
-void RtApiAsio :: stopStream()
-{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiAsio::stopStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
- }
-
- AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
- if ( handle->drainCounter == 0 ) {
- handle->drainCounter = 2;
- WaitForSingleObject( handle->condition, INFINITE ); // block until signaled
- }
- }
-
- stream_.state = STREAM_STOPPED;
-
- ASIOError result = ASIOStop();
- if ( result != ASE_OK ) {
- errorStream_ << "RtApiAsio::stopStream: error (" << getAsioErrorString( result ) << ") stopping device.";
- errorText_ = errorStream_.str();
- }
-
- if ( result == ASE_OK ) return;
- error( RtAudioError::SYSTEM_ERROR );
-}
-
-void RtApiAsio :: abortStream()
-{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiAsio::abortStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
- }
-
- // The following lines were commented-out because some behavior was
- // noted where the device buffers need to be zeroed to avoid
- // continuing sound, even when the device buffers are completely
- // disposed. So now, calling abort is the same as calling stop.
- // AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
- // handle->drainCounter = 2;
- stopStream();
-}
-
-// This function will be called by a spawned thread when the user
-// callback function signals that the stream should be stopped or
-// aborted. It is necessary to handle it this way because the
-// callbackEvent() function must return before the ASIOStop()
-// function will return.
-static unsigned __stdcall asioStopStream( void *ptr )
-{
- CallbackInfo *info = (CallbackInfo *) ptr;
- RtApiAsio *object = (RtApiAsio *) info->object;
-
- object->stopStream();
- _endthreadex( 0 );
- return 0;
-}
-
-bool RtApiAsio :: callbackEvent( long bufferIndex )
-{
- if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) return SUCCESS;
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiAsio::callbackEvent(): the stream is closed ... this shouldn't happen!";
- error( RtAudioError::WARNING );
- return FAILURE;
- }
-
- CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
- AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
-
- // Check if we were draining the stream and signal if finished.
- if ( handle->drainCounter > 3 ) {
-
- stream_.state = STREAM_STOPPING;
- if ( handle->internalDrain == false )
- SetEvent( handle->condition );
- else { // spawn a thread to stop the stream
- unsigned threadId;
- stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,
- &stream_.callbackInfo, 0, &threadId );
- }
- return SUCCESS;
- }
-
- // Invoke user callback to get fresh output data UNLESS we are
- // draining stream.
- if ( handle->drainCounter == 0 ) {
- RtAudioCallback callback = (RtAudioCallback) info->callback;
- double streamTime = getStreamTime();
- RtAudioStreamStatus status = 0;
- if ( stream_.mode != INPUT && asioXRun == true ) {
- status |= RTAUDIO_OUTPUT_UNDERFLOW;
- asioXRun = false;
- }
- if ( stream_.mode != OUTPUT && asioXRun == true ) {
- status |= RTAUDIO_INPUT_OVERFLOW;
- asioXRun = false;
- }
- int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
- stream_.bufferSize, streamTime, status, info->userData );
- if ( cbReturnValue == 2 ) {
- stream_.state = STREAM_STOPPING;
- handle->drainCounter = 2;
- unsigned threadId;
- stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &asioStopStream,
- &stream_.callbackInfo, 0, &threadId );
- return SUCCESS;
- }
- else if ( cbReturnValue == 1 ) {
- handle->drainCounter = 1;
- handle->internalDrain = true;
- }
- }
-
- unsigned int nChannels, bufferBytes, i, j;
- nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
-
- bufferBytes = stream_.bufferSize * formatBytes( stream_.deviceFormat[0] );
-
- if ( handle->drainCounter > 1 ) { // write zeros to the output stream
-
- for ( i=0, j=0; i<nChannels; i++ ) {
- if ( handle->bufferInfos[i].isInput != ASIOTrue )
- memset( handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes );
- }
-
- }
- else if ( stream_.doConvertBuffer[0] ) {
-
- convertBuffer( stream_.deviceBuffer, stream_.userBuffer[0], stream_.convertInfo[0] );
- if ( stream_.doByteSwap[0] )
- byteSwapBuffer( stream_.deviceBuffer,
- stream_.bufferSize * stream_.nDeviceChannels[0],
- stream_.deviceFormat[0] );
-
- for ( i=0, j=0; i<nChannels; i++ ) {
- if ( handle->bufferInfos[i].isInput != ASIOTrue )
- memcpy( handle->bufferInfos[i].buffers[bufferIndex],
- &stream_.deviceBuffer[j++*bufferBytes], bufferBytes );
- }
-
- }
- else {
-
- if ( stream_.doByteSwap[0] )
- byteSwapBuffer( stream_.userBuffer[0],
- stream_.bufferSize * stream_.nUserChannels[0],
- stream_.userFormat );
-
- for ( i=0, j=0; i<nChannels; i++ ) {
- if ( handle->bufferInfos[i].isInput != ASIOTrue )
- memcpy( handle->bufferInfos[i].buffers[bufferIndex],
- &stream_.userBuffer[0][bufferBytes*j++], bufferBytes );
- }
-
- }
- }
-
- // Don't bother draining input
- if ( handle->drainCounter ) {
- handle->drainCounter++;
- goto unlock;
- }
-
- if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
-
- bufferBytes = stream_.bufferSize * formatBytes(stream_.deviceFormat[1]);
-
- if (stream_.doConvertBuffer[1]) {
-
- // Always interleave ASIO input data.
- for ( i=0, j=0; i<nChannels; i++ ) {
- if ( handle->bufferInfos[i].isInput == ASIOTrue )
- memcpy( &stream_.deviceBuffer[j++*bufferBytes],
- handle->bufferInfos[i].buffers[bufferIndex],
- bufferBytes );
- }
-
- if ( stream_.doByteSwap[1] )
- byteSwapBuffer( stream_.deviceBuffer,
- stream_.bufferSize * stream_.nDeviceChannels[1],
- stream_.deviceFormat[1] );
- convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
-
- }
- else {
- for ( i=0, j=0; i<nChannels; i++ ) {
- if ( handle->bufferInfos[i].isInput == ASIOTrue ) {
- memcpy( &stream_.userBuffer[1][bufferBytes*j++],
- handle->bufferInfos[i].buffers[bufferIndex],
- bufferBytes );
- }
- }
-
- if ( stream_.doByteSwap[1] )
- byteSwapBuffer( stream_.userBuffer[1],
- stream_.bufferSize * stream_.nUserChannels[1],
- stream_.userFormat );
- }
- }
-
- unlock:
- // The following call was suggested by Malte Clasen. While the API
- // documentation indicates it should not be required, some device
- // drivers apparently do not function correctly without it.
- ASIOOutputReady();
-
- RtApi::tickStreamTime();
- return SUCCESS;
-}
-
-static void sampleRateChanged( ASIOSampleRate sRate )
-{
- // The ASIO documentation says that this usually only happens during
- // external sync. Audio processing is not stopped by the driver,
- // actual sample rate might not have even changed, maybe only the
- // sample rate status of an AES/EBU or S/PDIF digital input at the
- // audio device.
-
- RtApi *object = (RtApi *) asioCallbackInfo->object;
- try {
- object->stopStream();
- }
- catch ( RtAudioError &exception ) {
- std::cerr << "\nRtApiAsio: sampleRateChanged() error (" << exception.getMessage() << ")!\n" << std::endl;
- return;
- }
-
- std::cerr << "\nRtApiAsio: driver reports sample rate changed to " << sRate << " ... stream stopped!!!\n" << std::endl;
-}
-
-static long asioMessages( long selector, long value, void* /*message*/, double* /*opt*/ )
-{
- long ret = 0;
-
- switch( selector ) {
- case kAsioSelectorSupported:
- if ( value == kAsioResetRequest
- || value == kAsioEngineVersion
- || value == kAsioResyncRequest
- || value == kAsioLatenciesChanged
- // The following three were added for ASIO 2.0, you don't
- // necessarily have to support them.
- || value == kAsioSupportsTimeInfo
- || value == kAsioSupportsTimeCode
- || value == kAsioSupportsInputMonitor)
- ret = 1L;
- break;
- case kAsioResetRequest:
- // Defer the task and perform the reset of the driver during the
- // next "safe" situation. You cannot reset the driver right now,
- // as this code is called from the driver. Reset the driver is
- // done by completely destruct is. I.e. ASIOStop(),
- // ASIODisposeBuffers(), Destruction Afterwards you initialize the
- // driver again.
- std::cerr << "\nRtApiAsio: driver reset requested!!!" << std::endl;
- ret = 1L;
- break;
- case kAsioResyncRequest:
- // This informs the application that the driver encountered some
- // non-fatal data loss. It is used for synchronization purposes
- // of different media. Added mainly to work around the Win16Mutex
- // problems in Windows 95/98 with the Windows Multimedia system,
- // which could lose data because the Mutex was held too long by
- // another thread. However a driver can issue it in other
- // situations, too.
- // std::cerr << "\nRtApiAsio: driver resync requested!!!" << std::endl;
- asioXRun = true;
- ret = 1L;
- break;
- case kAsioLatenciesChanged:
- // This will inform the host application that the drivers were
- // latencies changed. Beware, it this does not mean that the
- // buffer sizes have changed! You might need to update internal
- // delay data.
- std::cerr << "\nRtApiAsio: driver latency may have changed!!!" << std::endl;
- ret = 1L;
- break;
- case kAsioEngineVersion:
- // Return the supported ASIO version of the host application. If
- // a host application does not implement this selector, ASIO 1.0
- // is assumed by the driver.
- ret = 2L;
- break;
- case kAsioSupportsTimeInfo:
- // Informs the driver whether the
- // asioCallbacks.bufferSwitchTimeInfo() callback is supported.
- // For compatibility with ASIO 1.0 drivers the host application
- // should always support the "old" bufferSwitch method, too.
- ret = 0;
- break;
- case kAsioSupportsTimeCode:
- // Informs the driver whether application is interested in time
- // code info. If an application does not need to know about time
- // code, the driver has less work to do.
- ret = 0;
- break;
- }
- return ret;
-}
-
-static const char* getAsioErrorString( ASIOError result )
-{
- struct Messages
- {
- ASIOError value;
- const char*message;
- };
-
- static const Messages m[] =
- {
- { ASE_NotPresent, "Hardware input or output is not present or available." },
- { ASE_HWMalfunction, "Hardware is malfunctioning." },
- { ASE_InvalidParameter, "Invalid input parameter." },
- { ASE_InvalidMode, "Invalid mode." },
- { ASE_SPNotAdvancing, "Sample position not advancing." },
- { ASE_NoClock, "Sample clock or rate cannot be determined or is not present." },
- { ASE_NoMemory, "Not enough memory to complete the request." }
- };
-
- for ( unsigned int i = 0; i < sizeof(m)/sizeof(m[0]); ++i )
- if ( m[i].value == result ) return m[i].message;
-
- return "Unknown error.";
-}
-
-//******************** End of __WINDOWS_ASIO__ *********************//
-#endif
-
-
-#if defined(__WINDOWS_WASAPI__) // Windows WASAPI API
-
-// Authored by Marcus Tomlinson <themarcustomlinson@gmail.com>, April 2014
-// - Introduces support for the Windows WASAPI API
-// - Aims to deliver bit streams to and from hardware at the lowest possible latency, via the absolute minimum buffer sizes required
-// - Provides flexible stream configuration to an otherwise strict and inflexible WASAPI interface
-// - Includes automatic internal conversion of sample rate and buffer size between hardware and the user
-
-#ifndef INITGUID
- #define INITGUID
-#endif
-
-#include <mfapi.h>
-#include <mferror.h>
-#include <mfplay.h>
-#include <mftransform.h>
-#include <wmcodecdsp.h>
-
-#include <audioclient.h>
-#include <avrt.h>
-#include <mmdeviceapi.h>
-#include <functiondiscoverykeys_devpkey.h>
-
-#ifndef MF_E_TRANSFORM_NEED_MORE_INPUT
- #define MF_E_TRANSFORM_NEED_MORE_INPUT _HRESULT_TYPEDEF_(0xc00d6d72)
-#endif
-
-#ifndef MFSTARTUP_NOSOCKET
- #define MFSTARTUP_NOSOCKET 0x1
-#endif
-
-#ifdef _MSC_VER
- #pragma comment( lib, "ksuser" )
- #pragma comment( lib, "mfplat.lib" )
- #pragma comment( lib, "mfuuid.lib" )
- #pragma comment( lib, "wmcodecdspuuid" )
-#endif
-
-//=============================================================================
-
-#define SAFE_RELEASE( objectPtr )\
-if ( objectPtr )\
-{\
- objectPtr->Release();\
- objectPtr = NULL;\
-}
-
-typedef HANDLE ( __stdcall *TAvSetMmThreadCharacteristicsPtr )( LPCWSTR TaskName, LPDWORD TaskIndex );
-
-//-----------------------------------------------------------------------------
-
-// WASAPI dictates stream sample rate, format, channel count, and in some cases, buffer size.
-// Therefore we must perform all necessary conversions to user buffers in order to satisfy these
-// requirements. WasapiBuffer ring buffers are used between HwIn->UserIn and UserOut->HwOut to
-// provide intermediate storage for read / write synchronization.
-class WasapiBuffer
-{
-public:
- WasapiBuffer()
- : buffer_( NULL ),
- bufferSize_( 0 ),
- inIndex_( 0 ),
- outIndex_( 0 ) {}
-
- ~WasapiBuffer() {
- free( buffer_ );
- }
-
- // sets the length of the internal ring buffer
- void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) {
- free( buffer_ );
-
- buffer_ = ( char* ) calloc( bufferSize, formatBytes );
-
- bufferSize_ = bufferSize;
- inIndex_ = 0;
- outIndex_ = 0;
- }
-
- // attempt to push a buffer into the ring buffer at the current "in" index
- bool pushBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )
- {
- if ( !buffer || // incoming buffer is NULL
- bufferSize == 0 || // incoming buffer has no data
- bufferSize > bufferSize_ ) // incoming buffer too large
- {
- return false;
- }
-
- unsigned int relOutIndex = outIndex_;
- unsigned int inIndexEnd = inIndex_ + bufferSize;
- if ( relOutIndex < inIndex_ && inIndexEnd >= bufferSize_ ) {
- relOutIndex += bufferSize_;
- }
-
- // the "IN" index CAN BEGIN at the "OUT" index
- // the "IN" index CANNOT END at the "OUT" index
- if ( inIndex_ < relOutIndex && inIndexEnd >= relOutIndex ) {
- return false; // not enough space between "in" index and "out" index
- }
-
- // copy buffer from external to internal
- int fromZeroSize = inIndex_ + bufferSize - bufferSize_;
- fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;
- int fromInSize = bufferSize - fromZeroSize;
-
- switch( format )
- {
- case RTAUDIO_SINT8:
- memcpy( &( ( char* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( char ) );
- memcpy( buffer_, &( ( char* ) buffer )[fromInSize], fromZeroSize * sizeof( char ) );
- break;
- case RTAUDIO_SINT16:
- memcpy( &( ( short* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( short ) );
- memcpy( buffer_, &( ( short* ) buffer )[fromInSize], fromZeroSize * sizeof( short ) );
- break;
- case RTAUDIO_SINT24:
- memcpy( &( ( S24* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( S24 ) );
- memcpy( buffer_, &( ( S24* ) buffer )[fromInSize], fromZeroSize * sizeof( S24 ) );
- break;
- case RTAUDIO_SINT32:
- memcpy( &( ( int* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( int ) );
- memcpy( buffer_, &( ( int* ) buffer )[fromInSize], fromZeroSize * sizeof( int ) );
- break;
- case RTAUDIO_FLOAT32:
- memcpy( &( ( float* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( float ) );
- memcpy( buffer_, &( ( float* ) buffer )[fromInSize], fromZeroSize * sizeof( float ) );
- break;
- case RTAUDIO_FLOAT64:
- memcpy( &( ( double* ) buffer_ )[inIndex_], buffer, fromInSize * sizeof( double ) );
- memcpy( buffer_, &( ( double* ) buffer )[fromInSize], fromZeroSize * sizeof( double ) );
- break;
- }
-
- // update "in" index
- inIndex_ += bufferSize;
- inIndex_ %= bufferSize_;
-
- return true;
- }
-
- // attempt to pull a buffer from the ring buffer from the current "out" index
- bool pullBuffer( char* buffer, unsigned int bufferSize, RtAudioFormat format )
- {
- if ( !buffer || // incoming buffer is NULL
- bufferSize == 0 || // incoming buffer has no data
- bufferSize > bufferSize_ ) // incoming buffer too large
- {
- return false;
- }
-
- unsigned int relInIndex = inIndex_;
- unsigned int outIndexEnd = outIndex_ + bufferSize;
- if ( relInIndex < outIndex_ && outIndexEnd >= bufferSize_ ) {
- relInIndex += bufferSize_;
- }
-
- // the "OUT" index CANNOT BEGIN at the "IN" index
- // the "OUT" index CAN END at the "IN" index
- if ( outIndex_ <= relInIndex && outIndexEnd > relInIndex ) {
- return false; // not enough space between "out" index and "in" index
- }
-
- // copy buffer from internal to external
- int fromZeroSize = outIndex_ + bufferSize - bufferSize_;
- fromZeroSize = fromZeroSize < 0 ? 0 : fromZeroSize;
- int fromOutSize = bufferSize - fromZeroSize;
-
- switch( format )
- {
- case RTAUDIO_SINT8:
- memcpy( buffer, &( ( char* ) buffer_ )[outIndex_], fromOutSize * sizeof( char ) );
- memcpy( &( ( char* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( char ) );
- break;
- case RTAUDIO_SINT16:
- memcpy( buffer, &( ( short* ) buffer_ )[outIndex_], fromOutSize * sizeof( short ) );
- memcpy( &( ( short* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( short ) );
- break;
- case RTAUDIO_SINT24:
- memcpy( buffer, &( ( S24* ) buffer_ )[outIndex_], fromOutSize * sizeof( S24 ) );
- memcpy( &( ( S24* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( S24 ) );
- break;
- case RTAUDIO_SINT32:
- memcpy( buffer, &( ( int* ) buffer_ )[outIndex_], fromOutSize * sizeof( int ) );
- memcpy( &( ( int* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( int ) );
- break;
- case RTAUDIO_FLOAT32:
- memcpy( buffer, &( ( float* ) buffer_ )[outIndex_], fromOutSize * sizeof( float ) );
- memcpy( &( ( float* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( float ) );
- break;
- case RTAUDIO_FLOAT64:
- memcpy( buffer, &( ( double* ) buffer_ )[outIndex_], fromOutSize * sizeof( double ) );
- memcpy( &( ( double* ) buffer )[fromOutSize], buffer_, fromZeroSize * sizeof( double ) );
- break;
- }
-
- // update "out" index
- outIndex_ += bufferSize;
- outIndex_ %= bufferSize_;
-
- return true;
- }
-
-private:
- char* buffer_;
- unsigned int bufferSize_;
- unsigned int inIndex_;
- unsigned int outIndex_;
-};
-
-//-----------------------------------------------------------------------------
-
-// In order to satisfy WASAPI's buffer requirements, we need a means of converting sample rate
-// between HW and the user. The WasapiResampler class is used to perform this conversion between
-// HwIn->UserIn and UserOut->HwOut during the stream callback loop.
-class WasapiResampler
-{
-public:
- WasapiResampler( bool isFloat, unsigned int bitsPerSample, unsigned int channelCount,
- unsigned int inSampleRate, unsigned int outSampleRate )
- : _bytesPerSample( bitsPerSample / 8 )
- , _channelCount( channelCount )
- , _sampleRatio( ( float ) outSampleRate / inSampleRate )
- , _transformUnk( NULL )
- , _transform( NULL )
- , _mediaType( NULL )
- , _inputMediaType( NULL )
- , _outputMediaType( NULL )
-
- #ifdef __IWMResamplerProps_FWD_DEFINED__
- , _resamplerProps( NULL )
- #endif
- {
- // 1. Initialization
-
- MFStartup( MF_VERSION, MFSTARTUP_NOSOCKET );
-
- // 2. Create Resampler Transform Object
-
- CoCreateInstance( CLSID_CResamplerMediaObject, NULL, CLSCTX_INPROC_SERVER,
- IID_IUnknown, ( void** ) &_transformUnk );
-
- _transformUnk->QueryInterface( IID_PPV_ARGS( &_transform ) );
-
- #ifdef __IWMResamplerProps_FWD_DEFINED__
- _transformUnk->QueryInterface( IID_PPV_ARGS( &_resamplerProps ) );
- _resamplerProps->SetHalfFilterLength( 60 ); // best conversion quality
- #endif
-
- // 3. Specify input / output format
-
- MFCreateMediaType( &_mediaType );
- _mediaType->SetGUID( MF_MT_MAJOR_TYPE, MFMediaType_Audio );
- _mediaType->SetGUID( MF_MT_SUBTYPE, isFloat ? MFAudioFormat_Float : MFAudioFormat_PCM );
- _mediaType->SetUINT32( MF_MT_AUDIO_NUM_CHANNELS, channelCount );
- _mediaType->SetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, inSampleRate );
- _mediaType->SetUINT32( MF_MT_AUDIO_BLOCK_ALIGNMENT, _bytesPerSample * channelCount );
- _mediaType->SetUINT32( MF_MT_AUDIO_AVG_BYTES_PER_SECOND, _bytesPerSample * channelCount * inSampleRate );
- _mediaType->SetUINT32( MF_MT_AUDIO_BITS_PER_SAMPLE, bitsPerSample );
- _mediaType->SetUINT32( MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE );
-
- MFCreateMediaType( &_inputMediaType );
- _mediaType->CopyAllItems( _inputMediaType );
-
- _transform->SetInputType( 0, _inputMediaType, 0 );
-
- MFCreateMediaType( &_outputMediaType );
- _mediaType->CopyAllItems( _outputMediaType );
-
- _outputMediaType->SetUINT32( MF_MT_AUDIO_SAMPLES_PER_SECOND, outSampleRate );
- _outputMediaType->SetUINT32( MF_MT_AUDIO_AVG_BYTES_PER_SECOND, _bytesPerSample * channelCount * outSampleRate );
-
- _transform->SetOutputType( 0, _outputMediaType, 0 );
-
- // 4. Send stream start messages to Resampler
-
- _transform->ProcessMessage( MFT_MESSAGE_COMMAND_FLUSH, 0 );
- _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0 );
- _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0 );
- }
-
- ~WasapiResampler()
- {
- // 8. Send stream stop messages to Resampler
-
- _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_END_OF_STREAM, 0 );
- _transform->ProcessMessage( MFT_MESSAGE_NOTIFY_END_STREAMING, 0 );
-
- // 9. Cleanup
-
- MFShutdown();
-
- SAFE_RELEASE( _transformUnk );
- SAFE_RELEASE( _transform );
- SAFE_RELEASE( _mediaType );
- SAFE_RELEASE( _inputMediaType );
- SAFE_RELEASE( _outputMediaType );
-
- #ifdef __IWMResamplerProps_FWD_DEFINED__
- SAFE_RELEASE( _resamplerProps );
- #endif
- }
-
- void Convert( char* outBuffer, const char* inBuffer, unsigned int inSampleCount, unsigned int& outSampleCount, int maxOutSampleCount = -1 )
- {
- unsigned int inputBufferSize = _bytesPerSample * _channelCount * inSampleCount;
- if ( _sampleRatio == 1 )
- {
- // no sample rate conversion required
- memcpy( outBuffer, inBuffer, inputBufferSize );
- outSampleCount = inSampleCount;
- return;
- }
-
- unsigned int outputBufferSize = 0;
- if ( maxOutSampleCount != -1 )
- {
- outputBufferSize = _bytesPerSample * _channelCount * maxOutSampleCount;
- }
- else
- {
- outputBufferSize = ( unsigned int ) ceilf( inputBufferSize * _sampleRatio ) + ( _bytesPerSample * _channelCount );
- }
-
- IMFMediaBuffer* rInBuffer;
- IMFSample* rInSample;
- BYTE* rInByteBuffer = NULL;
-
- // 5. Create Sample object from input data
-
- MFCreateMemoryBuffer( inputBufferSize, &rInBuffer );
-
- rInBuffer->Lock( &rInByteBuffer, NULL, NULL );
- memcpy( rInByteBuffer, inBuffer, inputBufferSize );
- rInBuffer->Unlock();
- rInByteBuffer = NULL;
-
- rInBuffer->SetCurrentLength( inputBufferSize );
-
- MFCreateSample( &rInSample );
- rInSample->AddBuffer( rInBuffer );
-
- // 6. Pass input data to Resampler
-
- _transform->ProcessInput( 0, rInSample, 0 );
-
- SAFE_RELEASE( rInBuffer );
- SAFE_RELEASE( rInSample );
-
- // 7. Perform sample rate conversion
-
- IMFMediaBuffer* rOutBuffer = NULL;
- BYTE* rOutByteBuffer = NULL;
-
- MFT_OUTPUT_DATA_BUFFER rOutDataBuffer;
- DWORD rStatus;
- DWORD rBytes = outputBufferSize; // maximum bytes accepted per ProcessOutput
-
- // 7.1 Create Sample object for output data
-
- memset( &rOutDataBuffer, 0, sizeof rOutDataBuffer );
- MFCreateSample( &( rOutDataBuffer.pSample ) );
- MFCreateMemoryBuffer( rBytes, &rOutBuffer );
- rOutDataBuffer.pSample->AddBuffer( rOutBuffer );
- rOutDataBuffer.dwStreamID = 0;
- rOutDataBuffer.dwStatus = 0;
- rOutDataBuffer.pEvents = NULL;
-
- // 7.2 Get output data from Resampler
-
- if ( _transform->ProcessOutput( 0, 1, &rOutDataBuffer, &rStatus ) == MF_E_TRANSFORM_NEED_MORE_INPUT )
- {
- outSampleCount = 0;
- SAFE_RELEASE( rOutBuffer );
- SAFE_RELEASE( rOutDataBuffer.pSample );
- return;
- }
-
- // 7.3 Write output data to outBuffer
-
- SAFE_RELEASE( rOutBuffer );
- rOutDataBuffer.pSample->ConvertToContiguousBuffer( &rOutBuffer );
- rOutBuffer->GetCurrentLength( &rBytes );
-
- rOutBuffer->Lock( &rOutByteBuffer, NULL, NULL );
- memcpy( outBuffer, rOutByteBuffer, rBytes );
- rOutBuffer->Unlock();
- rOutByteBuffer = NULL;
-
- outSampleCount = rBytes / _bytesPerSample / _channelCount;
- SAFE_RELEASE( rOutBuffer );
- SAFE_RELEASE( rOutDataBuffer.pSample );
- }
-
-private:
- unsigned int _bytesPerSample;
- unsigned int _channelCount;
- float _sampleRatio;
-
- IUnknown* _transformUnk;
- IMFTransform* _transform;
- IMFMediaType* _mediaType;
- IMFMediaType* _inputMediaType;
- IMFMediaType* _outputMediaType;
-
- #ifdef __IWMResamplerProps_FWD_DEFINED__
- IWMResamplerProps* _resamplerProps;
- #endif
-};
-
-//-----------------------------------------------------------------------------
-
-// A structure to hold various information related to the WASAPI implementation.
-struct WasapiHandle
-{
- IAudioClient* captureAudioClient;
- IAudioClient* renderAudioClient;
- IAudioCaptureClient* captureClient;
- IAudioRenderClient* renderClient;
- HANDLE captureEvent;
- HANDLE renderEvent;
-
- WasapiHandle()
- : captureAudioClient( NULL ),
- renderAudioClient( NULL ),
- captureClient( NULL ),
- renderClient( NULL ),
- captureEvent( NULL ),
- renderEvent( NULL ) {}
-};
-
-//=============================================================================
-
-RtApiWasapi::RtApiWasapi()
- : coInitialized_( false ), deviceEnumerator_( NULL )
-{
- // WASAPI can run either apartment or multi-threaded
- HRESULT hr = CoInitialize( NULL );
- if ( !FAILED( hr ) )
- coInitialized_ = true;
-
- // Instantiate device enumerator
- hr = CoCreateInstance( __uuidof( MMDeviceEnumerator ), NULL,
- CLSCTX_ALL, __uuidof( IMMDeviceEnumerator ),
- ( void** ) &deviceEnumerator_ );
-
- // If this runs on an old Windows, it will fail. Ignore and proceed.
- if ( FAILED( hr ) )
- deviceEnumerator_ = NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-RtApiWasapi::~RtApiWasapi()
-{
- if ( stream_.state != STREAM_CLOSED )
- closeStream();
-
- SAFE_RELEASE( deviceEnumerator_ );
-
- // If this object previously called CoInitialize()
- if ( coInitialized_ )
- CoUninitialize();
-}
-
-//=============================================================================
-
-unsigned int RtApiWasapi::getDeviceCount( void )
-{
- unsigned int captureDeviceCount = 0;
- unsigned int renderDeviceCount = 0;
-
- IMMDeviceCollection* captureDevices = NULL;
- IMMDeviceCollection* renderDevices = NULL;
-
- if ( !deviceEnumerator_ )
- return 0;
-
- // Count capture devices
- errorText_.clear();
- HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device collection.";
- goto Exit;
- }
-
- hr = captureDevices->GetCount( &captureDeviceCount );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve capture device count.";
- goto Exit;
- }
-
- // Count render devices
- hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device collection.";
- goto Exit;
- }
-
- hr = renderDevices->GetCount( &renderDeviceCount );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceCount: Unable to retrieve render device count.";
- goto Exit;
- }
-
-Exit:
- // release all references
- SAFE_RELEASE( captureDevices );
- SAFE_RELEASE( renderDevices );
-
- if ( errorText_.empty() )
- return captureDeviceCount + renderDeviceCount;
-
- error( RtAudioError::DRIVER_ERROR );
- return 0;
-}
-
-//-----------------------------------------------------------------------------
-
-RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
-{
- RtAudio::DeviceInfo info;
- unsigned int captureDeviceCount = 0;
- unsigned int renderDeviceCount = 0;
- std::string defaultDeviceName;
- bool isCaptureDevice = false;
-
- PROPVARIANT deviceNameProp;
- PROPVARIANT defaultDeviceNameProp;
-
- IMMDeviceCollection* captureDevices = NULL;
- IMMDeviceCollection* renderDevices = NULL;
- IMMDevice* devicePtr = NULL;
- IMMDevice* defaultDevicePtr = NULL;
- IAudioClient* audioClient = NULL;
- IPropertyStore* devicePropStore = NULL;
- IPropertyStore* defaultDevicePropStore = NULL;
-
- WAVEFORMATEX* deviceFormat = NULL;
- WAVEFORMATEX* closestMatchFormat = NULL;
-
- // probed
- info.probed = false;
-
- // Count capture devices
- errorText_.clear();
- RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;
- HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device collection.";
- goto Exit;
- }
-
- hr = captureDevices->GetCount( &captureDeviceCount );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device count.";
- goto Exit;
- }
-
- // Count render devices
- hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device collection.";
- goto Exit;
- }
-
- hr = renderDevices->GetCount( &renderDeviceCount );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device count.";
- goto Exit;
- }
-
- // validate device index
- if ( device >= captureDeviceCount + renderDeviceCount ) {
- errorText_ = "RtApiWasapi::getDeviceInfo: Invalid device index.";
- errorType = RtAudioError::INVALID_USE;
- goto Exit;
- }
-
- // determine whether index falls within capture or render devices
- if ( device >= renderDeviceCount ) {
- hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve capture device handle.";
- goto Exit;
- }
- isCaptureDevice = true;
- }
- else {
- hr = renderDevices->Item( device, &devicePtr );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve render device handle.";
- goto Exit;
- }
- isCaptureDevice = false;
- }
-
- // get default device name
- if ( isCaptureDevice ) {
- hr = deviceEnumerator_->GetDefaultAudioEndpoint( eCapture, eConsole, &defaultDevicePtr );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default capture device handle.";
- goto Exit;
- }
- }
- else {
- hr = deviceEnumerator_->GetDefaultAudioEndpoint( eRender, eConsole, &defaultDevicePtr );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default render device handle.";
- goto Exit;
- }
- }
-
- hr = defaultDevicePtr->OpenPropertyStore( STGM_READ, &defaultDevicePropStore );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open default device property store.";
- goto Exit;
- }
- PropVariantInit( &defaultDeviceNameProp );
-
- hr = defaultDevicePropStore->GetValue( PKEY_Device_FriendlyName, &defaultDeviceNameProp );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve default device property: PKEY_Device_FriendlyName.";
- goto Exit;
- }
-
- defaultDeviceName = convertCharPointerToStdString(defaultDeviceNameProp.pwszVal);
-
- // name
- hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceInfo: Unable to open device property store.";
- goto Exit;
- }
-
- PropVariantInit( &deviceNameProp );
-
- hr = devicePropStore->GetValue( PKEY_Device_FriendlyName, &deviceNameProp );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device property: PKEY_Device_FriendlyName.";
- goto Exit;
- }
-
- info.name =convertCharPointerToStdString(deviceNameProp.pwszVal);
-
- // is default
- if ( isCaptureDevice ) {
- info.isDefaultInput = info.name == defaultDeviceName;
- info.isDefaultOutput = false;
- }
- else {
- info.isDefaultInput = false;
- info.isDefaultOutput = info.name == defaultDeviceName;
- }
-
- // channel count
- hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL, NULL, ( void** ) &audioClient );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device audio client.";
- goto Exit;
- }
-
- hr = audioClient->GetMixFormat( &deviceFormat );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::getDeviceInfo: Unable to retrieve device mix format.";
- goto Exit;
- }
-
- if ( isCaptureDevice ) {
- info.inputChannels = deviceFormat->nChannels;
- info.outputChannels = 0;
- info.duplexChannels = 0;
- }
- else {
- info.inputChannels = 0;
- info.outputChannels = deviceFormat->nChannels;
- info.duplexChannels = 0;
- }
-
- // sample rates
- info.sampleRates.clear();
-
- // allow support for all sample rates as we have a built-in sample rate converter
- for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) {
- info.sampleRates.push_back( SAMPLE_RATES[i] );
- }
- info.preferredSampleRate = deviceFormat->nSamplesPerSec;
-
- // native format
- info.nativeFormats = 0;
-
- if ( deviceFormat->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
- ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
- ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT ) )
- {
- if ( deviceFormat->wBitsPerSample == 32 ) {
- info.nativeFormats |= RTAUDIO_FLOAT32;
- }
- else if ( deviceFormat->wBitsPerSample == 64 ) {
- info.nativeFormats |= RTAUDIO_FLOAT64;
- }
- }
- else if ( deviceFormat->wFormatTag == WAVE_FORMAT_PCM ||
- ( deviceFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
- ( ( WAVEFORMATEXTENSIBLE* ) deviceFormat )->SubFormat == KSDATAFORMAT_SUBTYPE_PCM ) )
- {
- if ( deviceFormat->wBitsPerSample == 8 ) {
- info.nativeFormats |= RTAUDIO_SINT8;
- }
- else if ( deviceFormat->wBitsPerSample == 16 ) {
- info.nativeFormats |= RTAUDIO_SINT16;
- }
- else if ( deviceFormat->wBitsPerSample == 24 ) {
- info.nativeFormats |= RTAUDIO_SINT24;
- }
- else if ( deviceFormat->wBitsPerSample == 32 ) {
- info.nativeFormats |= RTAUDIO_SINT32;
- }
- }
-
- // probed
- info.probed = true;
-
-Exit:
- // release all references
- PropVariantClear( &deviceNameProp );
- PropVariantClear( &defaultDeviceNameProp );
-
- SAFE_RELEASE( captureDevices );
- SAFE_RELEASE( renderDevices );
- SAFE_RELEASE( devicePtr );
- SAFE_RELEASE( defaultDevicePtr );
- SAFE_RELEASE( audioClient );
- SAFE_RELEASE( devicePropStore );
- SAFE_RELEASE( defaultDevicePropStore );
-
- CoTaskMemFree( deviceFormat );
- CoTaskMemFree( closestMatchFormat );
-
- if ( !errorText_.empty() )
- error( errorType );
- return info;
-}
-
-//-----------------------------------------------------------------------------
-
-unsigned int RtApiWasapi::getDefaultOutputDevice( void )
-{
- for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {
- if ( getDeviceInfo( i ).isDefaultOutput ) {
- return i;
- }
- }
-
- return 0;
-}
-
-//-----------------------------------------------------------------------------
-
-unsigned int RtApiWasapi::getDefaultInputDevice( void )
-{
- for ( unsigned int i = 0; i < getDeviceCount(); i++ ) {
- if ( getDeviceInfo( i ).isDefaultInput ) {
- return i;
- }
- }
-
- return 0;
-}
-
-//-----------------------------------------------------------------------------
-
-void RtApiWasapi::closeStream( void )
-{
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiWasapi::closeStream: No open stream to close.";
- error( RtAudioError::WARNING );
- return;
- }
-
- if ( stream_.state != STREAM_STOPPED )
- stopStream();
-
- // clean up stream memory
- SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient )
- SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient )
-
- SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->captureClient )
- SAFE_RELEASE( ( ( WasapiHandle* ) stream_.apiHandle )->renderClient )
-
- if ( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent )
- CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent );
-
- if ( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent )
- CloseHandle( ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent );
-
- delete ( WasapiHandle* ) stream_.apiHandle;
- stream_.apiHandle = NULL;
-
- for ( int i = 0; i < 2; i++ ) {
- if ( stream_.userBuffer[i] ) {
- free( stream_.userBuffer[i] );
- stream_.userBuffer[i] = 0;
- }
- }
-
- if ( stream_.deviceBuffer ) {
- free( stream_.deviceBuffer );
- stream_.deviceBuffer = 0;
- }
-
- // update stream state
- stream_.state = STREAM_CLOSED;
-}
-
-//-----------------------------------------------------------------------------
-
-void RtApiWasapi::startStream( void )
-{
- verifyStream();
-
- if ( stream_.state == STREAM_RUNNING ) {
- errorText_ = "RtApiWasapi::startStream: The stream is already running.";
- error( RtAudioError::WARNING );
- return;
- }
-
- #if defined( HAVE_GETTIMEOFDAY )
- gettimeofday( &stream_.lastTickTimestamp, NULL );
- #endif
-
- // update stream state
- stream_.state = STREAM_RUNNING;
-
- // create WASAPI stream thread
- stream_.callbackInfo.thread = ( ThreadHandle ) CreateThread( NULL, 0, runWasapiThread, this, CREATE_SUSPENDED, NULL );
-
- if ( !stream_.callbackInfo.thread ) {
- errorText_ = "RtApiWasapi::startStream: Unable to instantiate callback thread.";
- error( RtAudioError::THREAD_ERROR );
- }
- else {
- SetThreadPriority( ( void* ) stream_.callbackInfo.thread, stream_.callbackInfo.priority );
- ResumeThread( ( void* ) stream_.callbackInfo.thread );
- }
-}
-
-//-----------------------------------------------------------------------------
-
-void RtApiWasapi::stopStream( void )
-{
- verifyStream();
-
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiWasapi::stopStream: The stream is already stopped.";
- error( RtAudioError::WARNING );
- return;
- }
-
- // inform stream thread by setting stream state to STREAM_STOPPING
- stream_.state = STREAM_STOPPING;
-
- // wait until stream thread is stopped
- while( stream_.state != STREAM_STOPPED ) {
- Sleep( 1 );
- }
-
- // Wait for the last buffer to play before stopping.
- Sleep( 1000 * stream_.bufferSize / stream_.sampleRate );
-
- // close thread handle
- if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {
- errorText_ = "RtApiWasapi::stopStream: Unable to close callback thread.";
- error( RtAudioError::THREAD_ERROR );
- return;
- }
-
- stream_.callbackInfo.thread = (ThreadHandle) NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-void RtApiWasapi::abortStream( void )
-{
- verifyStream();
-
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiWasapi::abortStream: The stream is already stopped.";
- error( RtAudioError::WARNING );
- return;
- }
-
- // inform stream thread by setting stream state to STREAM_STOPPING
- stream_.state = STREAM_STOPPING;
-
- // wait until stream thread is stopped
- while ( stream_.state != STREAM_STOPPED ) {
- Sleep( 1 );
- }
-
- // close thread handle
- if ( stream_.callbackInfo.thread && !CloseHandle( ( void* ) stream_.callbackInfo.thread ) ) {
- errorText_ = "RtApiWasapi::abortStream: Unable to close callback thread.";
- error( RtAudioError::THREAD_ERROR );
- return;
- }
-
- stream_.callbackInfo.thread = (ThreadHandle) NULL;
-}
-
-//-----------------------------------------------------------------------------
-
-bool RtApiWasapi::probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
- RtAudioFormat format, unsigned int* bufferSize,
- RtAudio::StreamOptions* options )
-{
- bool methodResult = FAILURE;
- unsigned int captureDeviceCount = 0;
- unsigned int renderDeviceCount = 0;
-
- IMMDeviceCollection* captureDevices = NULL;
- IMMDeviceCollection* renderDevices = NULL;
- IMMDevice* devicePtr = NULL;
- WAVEFORMATEX* deviceFormat = NULL;
- unsigned int bufferBytes;
- stream_.state = STREAM_STOPPED;
-
- // create API Handle if not already created
- if ( !stream_.apiHandle )
- stream_.apiHandle = ( void* ) new WasapiHandle();
-
- // Count capture devices
- errorText_.clear();
- RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;
- HRESULT hr = deviceEnumerator_->EnumAudioEndpoints( eCapture, DEVICE_STATE_ACTIVE, &captureDevices );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device collection.";
- goto Exit;
- }
-
- hr = captureDevices->GetCount( &captureDeviceCount );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device count.";
- goto Exit;
- }
-
- // Count render devices
- hr = deviceEnumerator_->EnumAudioEndpoints( eRender, DEVICE_STATE_ACTIVE, &renderDevices );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device collection.";
- goto Exit;
- }
-
- hr = renderDevices->GetCount( &renderDeviceCount );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device count.";
- goto Exit;
- }
-
- // validate device index
- if ( device >= captureDeviceCount + renderDeviceCount ) {
- errorType = RtAudioError::INVALID_USE;
- errorText_ = "RtApiWasapi::probeDeviceOpen: Invalid device index.";
- goto Exit;
- }
-
- // if device index falls within capture devices
- if ( device >= renderDeviceCount ) {
- if ( mode != INPUT ) {
- errorType = RtAudioError::INVALID_USE;
- errorText_ = "RtApiWasapi::probeDeviceOpen: Capture device selected as output device.";
- goto Exit;
- }
-
- // retrieve captureAudioClient from devicePtr
- IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;
-
- hr = captureDevices->Item( device - renderDeviceCount, &devicePtr );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device handle.";
- goto Exit;
- }
-
- hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,
- NULL, ( void** ) &captureAudioClient );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device audio client.";
- goto Exit;
- }
-
- hr = captureAudioClient->GetMixFormat( &deviceFormat );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve capture device mix format.";
- goto Exit;
- }
-
- stream_.nDeviceChannels[mode] = deviceFormat->nChannels;
- captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );
- }
-
- // if device index falls within render devices and is configured for loopback
- if ( device < renderDeviceCount && mode == INPUT )
- {
- // if renderAudioClient is not initialised, initialise it now
- IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;
- if ( !renderAudioClient )
- {
- probeDeviceOpen( device, OUTPUT, channels, firstChannel, sampleRate, format, bufferSize, options );
- }
-
- // retrieve captureAudioClient from devicePtr
- IAudioClient*& captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;
-
- hr = renderDevices->Item( device, &devicePtr );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device handle.";
- goto Exit;
- }
-
- hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,
- NULL, ( void** ) &captureAudioClient );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device audio client.";
- goto Exit;
- }
-
- hr = captureAudioClient->GetMixFormat( &deviceFormat );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device mix format.";
- goto Exit;
- }
-
- stream_.nDeviceChannels[mode] = deviceFormat->nChannels;
- captureAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );
- }
-
- // if device index falls within render devices and is configured for output
- if ( device < renderDeviceCount && mode == OUTPUT )
- {
- // if renderAudioClient is already initialised, don't initialise it again
- IAudioClient*& renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;
- if ( renderAudioClient )
- {
- methodResult = SUCCESS;
- goto Exit;
- }
-
- hr = renderDevices->Item( device, &devicePtr );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device handle.";
- goto Exit;
- }
-
- hr = devicePtr->Activate( __uuidof( IAudioClient ), CLSCTX_ALL,
- NULL, ( void** ) &renderAudioClient );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device audio client.";
- goto Exit;
- }
-
- hr = renderAudioClient->GetMixFormat( &deviceFormat );
- if ( FAILED( hr ) ) {
- errorText_ = "RtApiWasapi::probeDeviceOpen: Unable to retrieve render device mix format.";
- goto Exit;
- }
-
- stream_.nDeviceChannels[mode] = deviceFormat->nChannels;
- renderAudioClient->GetStreamLatency( ( long long* ) &stream_.latency[mode] );
- }
-
- // fill stream data
- if ( ( stream_.mode == OUTPUT && mode == INPUT ) ||
- ( stream_.mode == INPUT && mode == OUTPUT ) ) {
- stream_.mode = DUPLEX;
- }
- else {
- stream_.mode = mode;
- }
-
- stream_.device[mode] = device;
- stream_.doByteSwap[mode] = false;
- stream_.sampleRate = sampleRate;
- stream_.bufferSize = *bufferSize;
- stream_.nBuffers = 1;
- stream_.nUserChannels[mode] = channels;
- stream_.channelOffset[mode] = firstChannel;
- stream_.userFormat = format;
- stream_.deviceFormat[mode] = getDeviceInfo( device ).nativeFormats;
-
- if ( options && options->flags & RTAUDIO_NONINTERLEAVED )
- stream_.userInterleaved = false;
- else
- stream_.userInterleaved = true;
- stream_.deviceInterleaved[mode] = true;
-
- // Set flags for buffer conversion.
- stream_.doConvertBuffer[mode] = false;
- if ( stream_.userFormat != stream_.deviceFormat[mode] ||
- stream_.nUserChannels[0] != stream_.nDeviceChannels[0] ||
- stream_.nUserChannels[1] != stream_.nDeviceChannels[1] )
- stream_.doConvertBuffer[mode] = true;
- else if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
- stream_.nUserChannels[mode] > 1 )
- stream_.doConvertBuffer[mode] = true;
-
- if ( stream_.doConvertBuffer[mode] )
- setConvertInfo( mode, firstChannel );
-
- // Allocate necessary internal buffers
- bufferBytes = stream_.nUserChannels[mode] * stream_.bufferSize * formatBytes( stream_.userFormat );
-
- stream_.userBuffer[mode] = ( char* ) calloc( bufferBytes, 1 );
- if ( !stream_.userBuffer[mode] ) {
- errorType = RtAudioError::MEMORY_ERROR;
- errorText_ = "RtApiWasapi::probeDeviceOpen: Error allocating user buffer memory.";
- goto Exit;
- }
-
- if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME )
- stream_.callbackInfo.priority = 15;
- else
- stream_.callbackInfo.priority = 0;
-
- ///! TODO: RTAUDIO_MINIMIZE_LATENCY // Provide stream buffers directly to callback
- ///! TODO: RTAUDIO_HOG_DEVICE // Exclusive mode
-
- methodResult = SUCCESS;
-
-Exit:
- //clean up
- SAFE_RELEASE( captureDevices );
- SAFE_RELEASE( renderDevices );
- SAFE_RELEASE( devicePtr );
- CoTaskMemFree( deviceFormat );
-
- // if method failed, close the stream
- if ( methodResult == FAILURE )
- closeStream();
-
- if ( !errorText_.empty() )
- error( errorType );
- return methodResult;
-}
-
-//=============================================================================
-
-DWORD WINAPI RtApiWasapi::runWasapiThread( void* wasapiPtr )
-{
- if ( wasapiPtr )
- ( ( RtApiWasapi* ) wasapiPtr )->wasapiThread();
-
- return 0;
-}
-
-DWORD WINAPI RtApiWasapi::stopWasapiThread( void* wasapiPtr )
-{
- if ( wasapiPtr )
- ( ( RtApiWasapi* ) wasapiPtr )->stopStream();
-
- return 0;
-}
-
-DWORD WINAPI RtApiWasapi::abortWasapiThread( void* wasapiPtr )
-{
- if ( wasapiPtr )
- ( ( RtApiWasapi* ) wasapiPtr )->abortStream();
-
- return 0;
-}
-
-//-----------------------------------------------------------------------------
-
-void RtApiWasapi::wasapiThread()
-{
- // as this is a new thread, we must CoInitialize it
- CoInitialize( NULL );
-
- HRESULT hr;
-
- IAudioClient* captureAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureAudioClient;
- IAudioClient* renderAudioClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderAudioClient;
- IAudioCaptureClient* captureClient = ( ( WasapiHandle* ) stream_.apiHandle )->captureClient;
- IAudioRenderClient* renderClient = ( ( WasapiHandle* ) stream_.apiHandle )->renderClient;
- HANDLE captureEvent = ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent;
- HANDLE renderEvent = ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent;
-
- WAVEFORMATEX* captureFormat = NULL;
- WAVEFORMATEX* renderFormat = NULL;
- float captureSrRatio = 0.0f;
- float renderSrRatio = 0.0f;
- WasapiBuffer captureBuffer;
- WasapiBuffer renderBuffer;
- WasapiResampler* captureResampler = NULL;
- WasapiResampler* renderResampler = NULL;
-
- // declare local stream variables
- RtAudioCallback callback = ( RtAudioCallback ) stream_.callbackInfo.callback;
- BYTE* streamBuffer = NULL;
- DWORD captureFlags = 0;
- unsigned int bufferFrameCount = 0;
- unsigned int numFramesPadding = 0;
- unsigned int convBufferSize = 0;
- bool loopbackEnabled = stream_.device[INPUT] == stream_.device[OUTPUT];
- bool callbackPushed = true;
- bool callbackPulled = false;
- bool callbackStopped = false;
- int callbackResult = 0;
-
- // convBuffer is used to store converted buffers between WASAPI and the user
- char* convBuffer = NULL;
- unsigned int convBuffSize = 0;
- unsigned int deviceBuffSize = 0;
-
- std::string errorText;
- RtAudioError::Type errorType = RtAudioError::DRIVER_ERROR;
-
- // Attempt to assign "Pro Audio" characteristic to thread
- HMODULE AvrtDll = LoadLibrary( (LPCTSTR) "AVRT.dll" );
- if ( AvrtDll ) {
- DWORD taskIndex = 0;
- TAvSetMmThreadCharacteristicsPtr AvSetMmThreadCharacteristicsPtr =
- ( TAvSetMmThreadCharacteristicsPtr ) (void(*)()) GetProcAddress( AvrtDll, "AvSetMmThreadCharacteristicsW" );
- AvSetMmThreadCharacteristicsPtr( L"Pro Audio", &taskIndex );
- FreeLibrary( AvrtDll );
- }
-
- // start capture stream if applicable
- if ( captureAudioClient ) {
- hr = captureAudioClient->GetMixFormat( &captureFormat );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";
- goto Exit;
- }
-
- // init captureResampler
- captureResampler = new WasapiResampler( stream_.deviceFormat[INPUT] == RTAUDIO_FLOAT32 || stream_.deviceFormat[INPUT] == RTAUDIO_FLOAT64,
- formatBytes( stream_.deviceFormat[INPUT] ) * 8, stream_.nDeviceChannels[INPUT],
- captureFormat->nSamplesPerSec, stream_.sampleRate );
-
- captureSrRatio = ( ( float ) captureFormat->nSamplesPerSec / stream_.sampleRate );
-
- if ( !captureClient ) {
- hr = captureAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,
- loopbackEnabled ? AUDCLNT_STREAMFLAGS_LOOPBACK : AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
- 0,
- 0,
- captureFormat,
- NULL );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to initialize capture audio client.";
- goto Exit;
- }
-
- hr = captureAudioClient->GetService( __uuidof( IAudioCaptureClient ),
- ( void** ) &captureClient );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to retrieve capture client handle.";
- goto Exit;
- }
-
- // don't configure captureEvent if in loopback mode
- if ( !loopbackEnabled )
- {
- // configure captureEvent to trigger on every available capture buffer
- captureEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
- if ( !captureEvent ) {
- errorType = RtAudioError::SYSTEM_ERROR;
- errorText = "RtApiWasapi::wasapiThread: Unable to create capture event.";
- goto Exit;
- }
-
- hr = captureAudioClient->SetEventHandle( captureEvent );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to set capture event handle.";
- goto Exit;
- }
-
- ( ( WasapiHandle* ) stream_.apiHandle )->captureEvent = captureEvent;
- }
-
- ( ( WasapiHandle* ) stream_.apiHandle )->captureClient = captureClient;
-
- // reset the capture stream
- hr = captureAudioClient->Reset();
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to reset capture stream.";
- goto Exit;
- }
-
- // start the capture stream
- hr = captureAudioClient->Start();
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to start capture stream.";
- goto Exit;
- }
- }
-
- unsigned int inBufferSize = 0;
- hr = captureAudioClient->GetBufferSize( &inBufferSize );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to get capture buffer size.";
- goto Exit;
- }
-
- // scale outBufferSize according to stream->user sample rate ratio
- unsigned int outBufferSize = ( unsigned int ) ceilf( stream_.bufferSize * captureSrRatio ) * stream_.nDeviceChannels[INPUT];
- inBufferSize *= stream_.nDeviceChannels[INPUT];
-
- // set captureBuffer size
- captureBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[INPUT] ) );
- }
-
- // start render stream if applicable
- if ( renderAudioClient ) {
- hr = renderAudioClient->GetMixFormat( &renderFormat );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to retrieve device mix format.";
- goto Exit;
- }
-
- // init renderResampler
- renderResampler = new WasapiResampler( stream_.deviceFormat[OUTPUT] == RTAUDIO_FLOAT32 || stream_.deviceFormat[OUTPUT] == RTAUDIO_FLOAT64,
- formatBytes( stream_.deviceFormat[OUTPUT] ) * 8, stream_.nDeviceChannels[OUTPUT],
- stream_.sampleRate, renderFormat->nSamplesPerSec );
-
- renderSrRatio = ( ( float ) renderFormat->nSamplesPerSec / stream_.sampleRate );
-
- if ( !renderClient ) {
- hr = renderAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED,
- AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
- 0,
- 0,
- renderFormat,
- NULL );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to initialize render audio client.";
- goto Exit;
- }
-
- hr = renderAudioClient->GetService( __uuidof( IAudioRenderClient ),
- ( void** ) &renderClient );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to retrieve render client handle.";
- goto Exit;
- }
-
- // configure renderEvent to trigger on every available render buffer
- renderEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
- if ( !renderEvent ) {
- errorType = RtAudioError::SYSTEM_ERROR;
- errorText = "RtApiWasapi::wasapiThread: Unable to create render event.";
- goto Exit;
- }
-
- hr = renderAudioClient->SetEventHandle( renderEvent );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to set render event handle.";
- goto Exit;
- }
-
- ( ( WasapiHandle* ) stream_.apiHandle )->renderClient = renderClient;
- ( ( WasapiHandle* ) stream_.apiHandle )->renderEvent = renderEvent;
-
- // reset the render stream
- hr = renderAudioClient->Reset();
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to reset render stream.";
- goto Exit;
- }
-
- // start the render stream
- hr = renderAudioClient->Start();
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to start render stream.";
- goto Exit;
- }
- }
-
- unsigned int outBufferSize = 0;
- hr = renderAudioClient->GetBufferSize( &outBufferSize );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to get render buffer size.";
- goto Exit;
- }
-
- // scale inBufferSize according to user->stream sample rate ratio
- unsigned int inBufferSize = ( unsigned int ) ceilf( stream_.bufferSize * renderSrRatio ) * stream_.nDeviceChannels[OUTPUT];
- outBufferSize *= stream_.nDeviceChannels[OUTPUT];
-
- // set renderBuffer size
- renderBuffer.setBufferSize( inBufferSize + outBufferSize, formatBytes( stream_.deviceFormat[OUTPUT] ) );
- }
-
- // malloc buffer memory
- if ( stream_.mode == INPUT )
- {
- using namespace std; // for ceilf
- convBuffSize = ( size_t ) ( ceilf( stream_.bufferSize * captureSrRatio ) ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );
- deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );
- }
- else if ( stream_.mode == OUTPUT )
- {
- convBuffSize = ( size_t ) ( ceilf( stream_.bufferSize * renderSrRatio ) ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );
- deviceBuffSize = stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] );
- }
- else if ( stream_.mode == DUPLEX )
- {
- convBuffSize = std::max( ( size_t ) ( ceilf( stream_.bufferSize * captureSrRatio ) ) * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),
- ( size_t ) ( ceilf( stream_.bufferSize * renderSrRatio ) ) * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );
- deviceBuffSize = std::max( stream_.bufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] ),
- stream_.bufferSize * stream_.nDeviceChannels[OUTPUT] * formatBytes( stream_.deviceFormat[OUTPUT] ) );
- }
-
- convBuffSize *= 2; // allow overflow for *SrRatio remainders
- convBuffer = ( char* ) calloc( convBuffSize, 1 );
- stream_.deviceBuffer = ( char* ) calloc( deviceBuffSize, 1 );
- if ( !convBuffer || !stream_.deviceBuffer ) {
- errorType = RtAudioError::MEMORY_ERROR;
- errorText = "RtApiWasapi::wasapiThread: Error allocating device buffer memory.";
- goto Exit;
- }
-
- // stream process loop
- while ( stream_.state != STREAM_STOPPING ) {
- if ( !callbackPulled ) {
- // Callback Input
- // ==============
- // 1. Pull callback buffer from inputBuffer
- // 2. If 1. was successful: Convert callback buffer to user sample rate and channel count
- // Convert callback buffer to user format
-
- if ( captureAudioClient )
- {
- int samplesToPull = ( unsigned int ) floorf( stream_.bufferSize * captureSrRatio );
-
- convBufferSize = 0;
- while ( convBufferSize < stream_.bufferSize )
- {
- // Pull callback buffer from inputBuffer
- callbackPulled = captureBuffer.pullBuffer( convBuffer,
- samplesToPull * stream_.nDeviceChannels[INPUT],
- stream_.deviceFormat[INPUT] );
-
- if ( !callbackPulled )
- {
- break;
- }
-
- // Convert callback buffer to user sample rate
- unsigned int deviceBufferOffset = convBufferSize * stream_.nDeviceChannels[INPUT] * formatBytes( stream_.deviceFormat[INPUT] );
- unsigned int convSamples = 0;
-
- captureResampler->Convert( stream_.deviceBuffer + deviceBufferOffset,
- convBuffer,
- samplesToPull,
- convSamples,
- convBufferSize == 0 ? -1 : stream_.bufferSize - convBufferSize );
-
- convBufferSize += convSamples;
- samplesToPull = 1; // now pull one sample at a time until we have stream_.bufferSize samples
- }
-
- if ( callbackPulled )
- {
- if ( stream_.doConvertBuffer[INPUT] ) {
- // Convert callback buffer to user format
- convertBuffer( stream_.userBuffer[INPUT],
- stream_.deviceBuffer,
- stream_.convertInfo[INPUT] );
- }
- else {
- // no further conversion, simple copy deviceBuffer to userBuffer
- memcpy( stream_.userBuffer[INPUT],
- stream_.deviceBuffer,
- stream_.bufferSize * stream_.nUserChannels[INPUT] * formatBytes( stream_.userFormat ) );
- }
- }
- }
- else {
- // if there is no capture stream, set callbackPulled flag
- callbackPulled = true;
- }
-
- // Execute Callback
- // ================
- // 1. Execute user callback method
- // 2. Handle return value from callback
-
- // if callback has not requested the stream to stop
- if ( callbackPulled && !callbackStopped ) {
- // Execute user callback method
- callbackResult = callback( stream_.userBuffer[OUTPUT],
- stream_.userBuffer[INPUT],
- stream_.bufferSize,
- getStreamTime(),
- captureFlags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY ? RTAUDIO_INPUT_OVERFLOW : 0,
- stream_.callbackInfo.userData );
-
- // tick stream time
- RtApi::tickStreamTime();
-
- // Handle return value from callback
- if ( callbackResult == 1 ) {
- // instantiate a thread to stop this thread
- HANDLE threadHandle = CreateThread( NULL, 0, stopWasapiThread, this, 0, NULL );
- if ( !threadHandle ) {
- errorType = RtAudioError::THREAD_ERROR;
- errorText = "RtApiWasapi::wasapiThread: Unable to instantiate stream stop thread.";
- goto Exit;
- }
- else if ( !CloseHandle( threadHandle ) ) {
- errorType = RtAudioError::THREAD_ERROR;
- errorText = "RtApiWasapi::wasapiThread: Unable to close stream stop thread handle.";
- goto Exit;
- }
-
- callbackStopped = true;
- }
- else if ( callbackResult == 2 ) {
- // instantiate a thread to stop this thread
- HANDLE threadHandle = CreateThread( NULL, 0, abortWasapiThread, this, 0, NULL );
- if ( !threadHandle ) {
- errorType = RtAudioError::THREAD_ERROR;
- errorText = "RtApiWasapi::wasapiThread: Unable to instantiate stream abort thread.";
- goto Exit;
- }
- else if ( !CloseHandle( threadHandle ) ) {
- errorType = RtAudioError::THREAD_ERROR;
- errorText = "RtApiWasapi::wasapiThread: Unable to close stream abort thread handle.";
- goto Exit;
- }
-
- callbackStopped = true;
- }
- }
- }
-
- // Callback Output
- // ===============
- // 1. Convert callback buffer to stream format
- // 2. Convert callback buffer to stream sample rate and channel count
- // 3. Push callback buffer into outputBuffer
-
- if ( renderAudioClient && callbackPulled )
- {
- // if the last call to renderBuffer.PushBuffer() was successful
- if ( callbackPushed || convBufferSize == 0 )
- {
- if ( stream_.doConvertBuffer[OUTPUT] )
- {
- // Convert callback buffer to stream format
- convertBuffer( stream_.deviceBuffer,
- stream_.userBuffer[OUTPUT],
- stream_.convertInfo[OUTPUT] );
-
- }
- else {
- // no further conversion, simple copy userBuffer to deviceBuffer
- memcpy( stream_.deviceBuffer,
- stream_.userBuffer[OUTPUT],
- stream_.bufferSize * stream_.nUserChannels[OUTPUT] * formatBytes( stream_.userFormat ) );
- }
-
- // Convert callback buffer to stream sample rate
- renderResampler->Convert( convBuffer,
- stream_.deviceBuffer,
- stream_.bufferSize,
- convBufferSize );
- }
-
- // Push callback buffer into outputBuffer
- callbackPushed = renderBuffer.pushBuffer( convBuffer,
- convBufferSize * stream_.nDeviceChannels[OUTPUT],
- stream_.deviceFormat[OUTPUT] );
- }
- else {
- // if there is no render stream, set callbackPushed flag
- callbackPushed = true;
- }
-
- // Stream Capture
- // ==============
- // 1. Get capture buffer from stream
- // 2. Push capture buffer into inputBuffer
- // 3. If 2. was successful: Release capture buffer
-
- if ( captureAudioClient ) {
- // if the callback input buffer was not pulled from captureBuffer, wait for next capture event
- if ( !callbackPulled ) {
- WaitForSingleObject( loopbackEnabled ? renderEvent : captureEvent, INFINITE );
- }
-
- // Get capture buffer from stream
- hr = captureClient->GetBuffer( &streamBuffer,
- &bufferFrameCount,
- &captureFlags, NULL, NULL );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to retrieve capture buffer.";
- goto Exit;
- }
-
- if ( bufferFrameCount != 0 ) {
- // Push capture buffer into inputBuffer
- if ( captureBuffer.pushBuffer( ( char* ) streamBuffer,
- bufferFrameCount * stream_.nDeviceChannels[INPUT],
- stream_.deviceFormat[INPUT] ) )
- {
- // Release capture buffer
- hr = captureClient->ReleaseBuffer( bufferFrameCount );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";
- goto Exit;
- }
- }
- else
- {
- // Inform WASAPI that capture was unsuccessful
- hr = captureClient->ReleaseBuffer( 0 );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";
- goto Exit;
- }
- }
- }
- else
- {
- // Inform WASAPI that capture was unsuccessful
- hr = captureClient->ReleaseBuffer( 0 );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to release capture buffer.";
- goto Exit;
- }
- }
- }
-
- // Stream Render
- // =============
- // 1. Get render buffer from stream
- // 2. Pull next buffer from outputBuffer
- // 3. If 2. was successful: Fill render buffer with next buffer
- // Release render buffer
-
- if ( renderAudioClient ) {
- // if the callback output buffer was not pushed to renderBuffer, wait for next render event
- if ( callbackPulled && !callbackPushed ) {
- WaitForSingleObject( renderEvent, INFINITE );
- }
-
- // Get render buffer from stream
- hr = renderAudioClient->GetBufferSize( &bufferFrameCount );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer size.";
- goto Exit;
- }
-
- hr = renderAudioClient->GetCurrentPadding( &numFramesPadding );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer padding.";
- goto Exit;
- }
-
- bufferFrameCount -= numFramesPadding;
-
- if ( bufferFrameCount != 0 ) {
- hr = renderClient->GetBuffer( bufferFrameCount, &streamBuffer );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to retrieve render buffer.";
- goto Exit;
- }
-
- // Pull next buffer from outputBuffer
- // Fill render buffer with next buffer
- if ( renderBuffer.pullBuffer( ( char* ) streamBuffer,
- bufferFrameCount * stream_.nDeviceChannels[OUTPUT],
- stream_.deviceFormat[OUTPUT] ) )
- {
- // Release render buffer
- hr = renderClient->ReleaseBuffer( bufferFrameCount, 0 );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to release render buffer.";
- goto Exit;
- }
- }
- else
- {
- // Inform WASAPI that render was unsuccessful
- hr = renderClient->ReleaseBuffer( 0, 0 );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to release render buffer.";
- goto Exit;
- }
- }
- }
- else
- {
- // Inform WASAPI that render was unsuccessful
- hr = renderClient->ReleaseBuffer( 0, 0 );
- if ( FAILED( hr ) ) {
- errorText = "RtApiWasapi::wasapiThread: Unable to release render buffer.";
- goto Exit;
- }
- }
- }
-
- // if the callback buffer was pushed renderBuffer reset callbackPulled flag
- if ( callbackPushed ) {
- // unsetting the callbackPulled flag lets the stream know that
- // the audio device is ready for another callback output buffer.
- callbackPulled = false;
- }
-
- }
-
-Exit:
- // clean up
- CoTaskMemFree( captureFormat );
- CoTaskMemFree( renderFormat );
-
- free ( convBuffer );
- delete renderResampler;
- delete captureResampler;
-
- CoUninitialize();
-
- // update stream state
- stream_.state = STREAM_STOPPED;
-
- if ( !errorText.empty() )
- {
- errorText_ = errorText;
- error( errorType );
- }
-}
-
-//******************** End of __WINDOWS_WASAPI__ *********************//
-#endif
-
-
-#if defined(__WINDOWS_DS__) // Windows DirectSound API
-
-// Modified by Robin Davies, October 2005
-// - Improvements to DirectX pointer chasing.
-// - Bug fix for non-power-of-two Asio granularity used by Edirol PCR-A30.
-// - Auto-call CoInitialize for DSOUND and ASIO platforms.
-// Various revisions for RtAudio 4.0 by Gary Scavone, April 2007
-// Changed device query structure for RtAudio 4.0.7, January 2010
-
-#include <windows.h>
-#include <process.h>
-#include <mmsystem.h>
-#include <mmreg.h>
-#include <dsound.h>
-#include <assert.h>
-#include <algorithm>
-
-#if defined(__MINGW32__)
- // missing from latest mingw winapi
-#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
-#define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */
-#define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */
-#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
-#endif
-
-#define MINIMUM_DEVICE_BUFFER_SIZE 32768
-
-#ifdef _MSC_VER // if Microsoft Visual C++
-#pragma comment( lib, "winmm.lib" ) // then, auto-link winmm.lib. Otherwise, it has to be added manually.
-#endif
-
-static inline DWORD dsPointerBetween( DWORD pointer, DWORD laterPointer, DWORD earlierPointer, DWORD bufferSize )
-{
- if ( pointer > bufferSize ) pointer -= bufferSize;
- if ( laterPointer < earlierPointer ) laterPointer += bufferSize;
- if ( pointer < earlierPointer ) pointer += bufferSize;
- return pointer >= earlierPointer && pointer < laterPointer;
-}
-
-// A structure to hold various information related to the DirectSound
-// API implementation.
-struct DsHandle {
- unsigned int drainCounter; // Tracks callback counts when draining
- bool internalDrain; // Indicates if stop is initiated from callback or not.
- void *id[2];
- void *buffer[2];
- bool xrun[2];
- UINT bufferPointer[2];
- DWORD dsBufferSize[2];
- DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by.
- HANDLE condition;
-
- DsHandle()
- :drainCounter(0), internalDrain(false) { id[0] = 0; id[1] = 0; buffer[0] = 0; buffer[1] = 0; xrun[0] = false; xrun[1] = false; bufferPointer[0] = 0; bufferPointer[1] = 0; }
-};
-
-// Declarations for utility functions, callbacks, and structures
-// specific to the DirectSound implementation.
-static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
- LPCTSTR description,
- LPCTSTR module,
- LPVOID lpContext );
-
-static const char* getErrorString( int code );
-
-static unsigned __stdcall callbackHandler( void *ptr );
-
-struct DsDevice {
- LPGUID id[2];
- bool validId[2];
- bool found;
- std::string name;
-
- DsDevice()
- : found(false) { validId[0] = false; validId[1] = false; }
-};
-
-struct DsProbeData {
- bool isInput;
- std::vector<struct DsDevice>* dsDevices;
-};
-
-RtApiDs :: RtApiDs()
-{
- // Dsound will run both-threaded. If CoInitialize fails, then just
- // accept whatever the mainline chose for a threading model.
- coInitialized_ = false;
- HRESULT hr = CoInitialize( NULL );
- if ( !FAILED( hr ) ) coInitialized_ = true;
-}
-
-RtApiDs :: ~RtApiDs()
-{
- if ( stream_.state != STREAM_CLOSED ) closeStream();
- if ( coInitialized_ ) CoUninitialize(); // balanced call.
-}
-
-// The DirectSound default output is always the first device.
-unsigned int RtApiDs :: getDefaultOutputDevice( void )
-{
- return 0;
-}
-
-// The DirectSound default input is always the first input device,
-// which is the first capture device enumerated.
-unsigned int RtApiDs :: getDefaultInputDevice( void )
-{
- return 0;
-}
-
-unsigned int RtApiDs :: getDeviceCount( void )
-{
- // Set query flag for previously found devices to false, so that we
- // can check for any devices that have disappeared.
- for ( unsigned int i=0; i<dsDevices.size(); i++ )
- dsDevices[i].found = false;
-
- // Query DirectSound devices.
- struct DsProbeData probeInfo;
- probeInfo.isInput = false;
- probeInfo.dsDevices = &dsDevices;
- HRESULT result = DirectSoundEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating output devices!";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- }
-
- // Query DirectSoundCapture devices.
- probeInfo.isInput = true;
- result = DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::getDeviceCount: error (" << getErrorString( result ) << ") enumerating input devices!";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- }
-
- // Clean out any devices that may have disappeared (code update submitted by Eli Zehngut).
- for ( unsigned int i=0; i<dsDevices.size(); ) {
- if ( dsDevices[i].found == false ) dsDevices.erase( dsDevices.begin() + i );
- else i++;
- }
-
- return static_cast<unsigned int>(dsDevices.size());
-}
-
-RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
-{
- RtAudio::DeviceInfo info;
- info.probed = false;
-
- if ( dsDevices.size() == 0 ) {
- // Force a query of all devices
- getDeviceCount();
- if ( dsDevices.size() == 0 ) {
- errorText_ = "RtApiDs::getDeviceInfo: no devices found!";
- error( RtAudioError::INVALID_USE );
- return info;
- }
- }
-
- if ( device >= dsDevices.size() ) {
- errorText_ = "RtApiDs::getDeviceInfo: device ID is invalid!";
- error( RtAudioError::INVALID_USE );
- return info;
- }
-
- HRESULT result;
- if ( dsDevices[ device ].validId[0] == false ) goto probeInput;
-
- LPDIRECTSOUND output;
- DSCAPS outCaps;
- result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- goto probeInput;
- }
-
- outCaps.dwSize = sizeof( outCaps );
- result = output->GetCaps( &outCaps );
- if ( FAILED( result ) ) {
- output->Release();
- errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting capabilities!";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- goto probeInput;
- }
-
- // Get output channel information.
- info.outputChannels = ( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1;
-
- // Get sample rate information.
- info.sampleRates.clear();
- for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
- if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&
- SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate ) {
- info.sampleRates.push_back( SAMPLE_RATES[k] );
-
- if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
- info.preferredSampleRate = SAMPLE_RATES[k];
- }
- }
-
- // Get format information.
- if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT ) info.nativeFormats |= RTAUDIO_SINT16;
- if ( outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) info.nativeFormats |= RTAUDIO_SINT8;
-
- output->Release();
-
- if ( getDefaultOutputDevice() == device )
- info.isDefaultOutput = true;
-
- if ( dsDevices[ device ].validId[1] == false ) {
- info.name = dsDevices[ device ].name;
- info.probed = true;
- return info;
- }
-
- probeInput:
-
- LPDIRECTSOUNDCAPTURE input;
- result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- DSCCAPS inCaps;
- inCaps.dwSize = sizeof( inCaps );
- result = input->GetCaps( &inCaps );
- if ( FAILED( result ) ) {
- input->Release();
- errorStream_ << "RtApiDs::getDeviceInfo: error (" << getErrorString( result ) << ") getting object capabilities (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- // Get input channel information.
- info.inputChannels = inCaps.dwChannels;
-
- // Get sample rate and format information.
- std::vector<unsigned int> rates;
- if ( inCaps.dwChannels >= 2 ) {
- if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) info.nativeFormats |= RTAUDIO_SINT16;
- if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) info.nativeFormats |= RTAUDIO_SINT16;
- if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) info.nativeFormats |= RTAUDIO_SINT16;
- if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) info.nativeFormats |= RTAUDIO_SINT16;
- if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) info.nativeFormats |= RTAUDIO_SINT8;
- if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) info.nativeFormats |= RTAUDIO_SINT8;
- if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) info.nativeFormats |= RTAUDIO_SINT8;
- if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) info.nativeFormats |= RTAUDIO_SINT8;
-
- if ( info.nativeFormats & RTAUDIO_SINT16 ) {
- if ( inCaps.dwFormats & WAVE_FORMAT_1S16 ) rates.push_back( 11025 );
- if ( inCaps.dwFormats & WAVE_FORMAT_2S16 ) rates.push_back( 22050 );
- if ( inCaps.dwFormats & WAVE_FORMAT_4S16 ) rates.push_back( 44100 );
- if ( inCaps.dwFormats & WAVE_FORMAT_96S16 ) rates.push_back( 96000 );
- }
- else if ( info.nativeFormats & RTAUDIO_SINT8 ) {
- if ( inCaps.dwFormats & WAVE_FORMAT_1S08 ) rates.push_back( 11025 );
- if ( inCaps.dwFormats & WAVE_FORMAT_2S08 ) rates.push_back( 22050 );
- if ( inCaps.dwFormats & WAVE_FORMAT_4S08 ) rates.push_back( 44100 );
- if ( inCaps.dwFormats & WAVE_FORMAT_96S08 ) rates.push_back( 96000 );
- }
- }
- else if ( inCaps.dwChannels == 1 ) {
- if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) info.nativeFormats |= RTAUDIO_SINT16;
- if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) info.nativeFormats |= RTAUDIO_SINT16;
- if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) info.nativeFormats |= RTAUDIO_SINT16;
- if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) info.nativeFormats |= RTAUDIO_SINT16;
- if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) info.nativeFormats |= RTAUDIO_SINT8;
- if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) info.nativeFormats |= RTAUDIO_SINT8;
- if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) info.nativeFormats |= RTAUDIO_SINT8;
- if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) info.nativeFormats |= RTAUDIO_SINT8;
-
- if ( info.nativeFormats & RTAUDIO_SINT16 ) {
- if ( inCaps.dwFormats & WAVE_FORMAT_1M16 ) rates.push_back( 11025 );
- if ( inCaps.dwFormats & WAVE_FORMAT_2M16 ) rates.push_back( 22050 );
- if ( inCaps.dwFormats & WAVE_FORMAT_4M16 ) rates.push_back( 44100 );
- if ( inCaps.dwFormats & WAVE_FORMAT_96M16 ) rates.push_back( 96000 );
- }
- else if ( info.nativeFormats & RTAUDIO_SINT8 ) {
- if ( inCaps.dwFormats & WAVE_FORMAT_1M08 ) rates.push_back( 11025 );
- if ( inCaps.dwFormats & WAVE_FORMAT_2M08 ) rates.push_back( 22050 );
- if ( inCaps.dwFormats & WAVE_FORMAT_4M08 ) rates.push_back( 44100 );
- if ( inCaps.dwFormats & WAVE_FORMAT_96M08 ) rates.push_back( 96000 );
- }
- }
- else info.inputChannels = 0; // technically, this would be an error
-
- input->Release();
-
- if ( info.inputChannels == 0 ) return info;
-
- // Copy the supported rates to the info structure but avoid duplication.
- bool found;
- for ( unsigned int i=0; i<rates.size(); i++ ) {
- found = false;
- for ( unsigned int j=0; j<info.sampleRates.size(); j++ ) {
- if ( rates[i] == info.sampleRates[j] ) {
- found = true;
- break;
- }
- }
- if ( found == false ) info.sampleRates.push_back( rates[i] );
- }
- std::sort( info.sampleRates.begin(), info.sampleRates.end() );
-
- // If device opens for both playback and capture, we determine the channels.
- if ( info.outputChannels > 0 && info.inputChannels > 0 )
- info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
-
- if ( device == 0 ) info.isDefaultInput = true;
-
- // Copy name and return.
- info.name = dsDevices[ device ].name;
- info.probed = true;
- return info;
-}
-
-bool RtApiDs :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
- RtAudioFormat format, unsigned int *bufferSize,
- RtAudio::StreamOptions *options )
-{
- if ( channels + firstChannel > 2 ) {
- errorText_ = "RtApiDs::probeDeviceOpen: DirectSound does not support more than 2 channels per device.";
- return FAILURE;
- }
-
- size_t nDevices = dsDevices.size();
- if ( nDevices == 0 ) {
- // This should not happen because a check is made before this function is called.
- errorText_ = "RtApiDs::probeDeviceOpen: no devices found!";
- return FAILURE;
- }
-
- if ( device >= nDevices ) {
- // This should not happen because a check is made before this function is called.
- errorText_ = "RtApiDs::probeDeviceOpen: device ID is invalid!";
- return FAILURE;
- }
-
- if ( mode == OUTPUT ) {
- if ( dsDevices[ device ].validId[0] == false ) {
- errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support output!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- }
- else { // mode == INPUT
- if ( dsDevices[ device ].validId[1] == false ) {
- errorStream_ << "RtApiDs::probeDeviceOpen: device (" << device << ") does not support input!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- }
-
- // According to a note in PortAudio, using GetDesktopWindow()
- // instead of GetForegroundWindow() is supposed to avoid problems
- // that occur when the application's window is not the foreground
- // window. Also, if the application window closes before the
- // DirectSound buffer, DirectSound can crash. In the past, I had
- // problems when using GetDesktopWindow() but it seems fine now
- // (January 2010). I'll leave it commented here.
- // HWND hWnd = GetForegroundWindow();
- HWND hWnd = GetDesktopWindow();
-
- // Check the numberOfBuffers parameter and limit the lowest value to
- // two. This is a judgement call and a value of two is probably too
- // low for capture, but it should work for playback.
- int nBuffers = 0;
- if ( options ) nBuffers = options->numberOfBuffers;
- if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) nBuffers = 2;
- if ( nBuffers < 2 ) nBuffers = 3;
-
- // Check the lower range of the user-specified buffer size and set
- // (arbitrarily) to a lower bound of 32.
- if ( *bufferSize < 32 ) *bufferSize = 32;
-
- // Create the wave format structure. The data format setting will
- // be determined later.
- WAVEFORMATEX waveFormat;
- ZeroMemory( &waveFormat, sizeof(WAVEFORMATEX) );
- waveFormat.wFormatTag = WAVE_FORMAT_PCM;
- waveFormat.nChannels = channels + firstChannel;
- waveFormat.nSamplesPerSec = (unsigned long) sampleRate;
-
- // Determine the device buffer size. By default, we'll use the value
- // defined above (32K), but we will grow it to make allowances for
- // very large software buffer sizes.
- DWORD dsBufferSize = MINIMUM_DEVICE_BUFFER_SIZE;
- DWORD dsPointerLeadTime = 0;
-
- void *ohandle = 0, *bhandle = 0;
- HRESULT result;
- if ( mode == OUTPUT ) {
-
- LPDIRECTSOUND output;
- result = DirectSoundCreate( dsDevices[ device ].id[0], &output, NULL );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening output device (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- DSCAPS outCaps;
- outCaps.dwSize = sizeof( outCaps );
- result = output->GetCaps( &outCaps );
- if ( FAILED( result ) ) {
- output->Release();
- errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting capabilities (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Check channel information.
- if ( channels + firstChannel == 2 && !( outCaps.dwFlags & DSCAPS_PRIMARYSTEREO ) ) {
- errorStream_ << "RtApiDs::getDeviceInfo: the output device (" << dsDevices[ device ].name << ") does not support stereo playback.";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Check format information. Use 16-bit format unless not
- // supported or user requests 8-bit.
- if ( outCaps.dwFlags & DSCAPS_PRIMARY16BIT &&
- !( format == RTAUDIO_SINT8 && outCaps.dwFlags & DSCAPS_PRIMARY8BIT ) ) {
- waveFormat.wBitsPerSample = 16;
- stream_.deviceFormat[mode] = RTAUDIO_SINT16;
- }
- else {
- waveFormat.wBitsPerSample = 8;
- stream_.deviceFormat[mode] = RTAUDIO_SINT8;
- }
- stream_.userFormat = format;
-
- // Update wave format structure and buffer information.
- waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
- waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
- dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;
-
- // If the user wants an even bigger buffer, increase the device buffer size accordingly.
- while ( dsPointerLeadTime * 2U > dsBufferSize )
- dsBufferSize *= 2;
-
- // Set cooperative level to DSSCL_EXCLUSIVE ... sound stops when window focus changes.
- // result = output->SetCooperativeLevel( hWnd, DSSCL_EXCLUSIVE );
- // Set cooperative level to DSSCL_PRIORITY ... sound remains when window focus changes.
- result = output->SetCooperativeLevel( hWnd, DSSCL_PRIORITY );
- if ( FAILED( result ) ) {
- output->Release();
- errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting cooperative level (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Even though we will write to the secondary buffer, we need to
- // access the primary buffer to set the correct output format
- // (since the default is 8-bit, 22 kHz!). Setup the DS primary
- // buffer description.
- DSBUFFERDESC bufferDescription;
- ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );
- bufferDescription.dwSize = sizeof( DSBUFFERDESC );
- bufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
-
- // Obtain the primary buffer
- LPDIRECTSOUNDBUFFER buffer;
- result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
- if ( FAILED( result ) ) {
- output->Release();
- errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") accessing primary buffer (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Set the primary DS buffer sound format.
- result = buffer->SetFormat( &waveFormat );
- if ( FAILED( result ) ) {
- output->Release();
- errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") setting primary buffer format (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Setup the secondary DS buffer description.
- ZeroMemory( &bufferDescription, sizeof( DSBUFFERDESC ) );
- bufferDescription.dwSize = sizeof( DSBUFFERDESC );
- bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
- DSBCAPS_GLOBALFOCUS |
- DSBCAPS_GETCURRENTPOSITION2 |
- DSBCAPS_LOCHARDWARE ); // Force hardware mixing
- bufferDescription.dwBufferBytes = dsBufferSize;
- bufferDescription.lpwfxFormat = &waveFormat;
-
- // Try to create the secondary DS buffer. If that doesn't work,
- // try to use software mixing. Otherwise, there's a problem.
- result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
- if ( FAILED( result ) ) {
- bufferDescription.dwFlags = ( DSBCAPS_STICKYFOCUS |
- DSBCAPS_GLOBALFOCUS |
- DSBCAPS_GETCURRENTPOSITION2 |
- DSBCAPS_LOCSOFTWARE ); // Force software mixing
- result = output->CreateSoundBuffer( &bufferDescription, &buffer, NULL );
- if ( FAILED( result ) ) {
- output->Release();
- errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating secondary buffer (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- }
-
- // Get the buffer size ... might be different from what we specified.
- DSBCAPS dsbcaps;
- dsbcaps.dwSize = sizeof( DSBCAPS );
- result = buffer->GetCaps( &dsbcaps );
- if ( FAILED( result ) ) {
- output->Release();
- buffer->Release();
- errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- dsBufferSize = dsbcaps.dwBufferBytes;
-
- // Lock the DS buffer
- LPVOID audioPtr;
- DWORD dataLen;
- result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );
- if ( FAILED( result ) ) {
- output->Release();
- buffer->Release();
- errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking buffer (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Zero the DS buffer
- ZeroMemory( audioPtr, dataLen );
-
- // Unlock the DS buffer
- result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
- if ( FAILED( result ) ) {
- output->Release();
- buffer->Release();
- errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking buffer (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- ohandle = (void *) output;
- bhandle = (void *) buffer;
- }
-
- if ( mode == INPUT ) {
-
- LPDIRECTSOUNDCAPTURE input;
- result = DirectSoundCaptureCreate( dsDevices[ device ].id[1], &input, NULL );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") opening input device (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- DSCCAPS inCaps;
- inCaps.dwSize = sizeof( inCaps );
- result = input->GetCaps( &inCaps );
- if ( FAILED( result ) ) {
- input->Release();
- errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting input capabilities (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Check channel information.
- if ( inCaps.dwChannels < channels + firstChannel ) {
- errorText_ = "RtApiDs::getDeviceInfo: the input device does not support requested input channels.";
- return FAILURE;
- }
-
- // Check format information. Use 16-bit format unless user
- // requests 8-bit.
- DWORD deviceFormats;
- if ( channels + firstChannel == 2 ) {
- deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08;
- if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {
- waveFormat.wBitsPerSample = 8;
- stream_.deviceFormat[mode] = RTAUDIO_SINT8;
- }
- else { // assume 16-bit is supported
- waveFormat.wBitsPerSample = 16;
- stream_.deviceFormat[mode] = RTAUDIO_SINT16;
- }
- }
- else { // channel == 1
- deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08;
- if ( format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats ) {
- waveFormat.wBitsPerSample = 8;
- stream_.deviceFormat[mode] = RTAUDIO_SINT8;
- }
- else { // assume 16-bit is supported
- waveFormat.wBitsPerSample = 16;
- stream_.deviceFormat[mode] = RTAUDIO_SINT16;
- }
- }
- stream_.userFormat = format;
-
- // Update wave format structure and buffer information.
- waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
- waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
- dsPointerLeadTime = nBuffers * (*bufferSize) * (waveFormat.wBitsPerSample / 8) * channels;
-
- // If the user wants an even bigger buffer, increase the device buffer size accordingly.
- while ( dsPointerLeadTime * 2U > dsBufferSize )
- dsBufferSize *= 2;
-
- // Setup the secondary DS buffer description.
- DSCBUFFERDESC bufferDescription;
- ZeroMemory( &bufferDescription, sizeof( DSCBUFFERDESC ) );
- bufferDescription.dwSize = sizeof( DSCBUFFERDESC );
- bufferDescription.dwFlags = 0;
- bufferDescription.dwReserved = 0;
- bufferDescription.dwBufferBytes = dsBufferSize;
- bufferDescription.lpwfxFormat = &waveFormat;
-
- // Create the capture buffer.
- LPDIRECTSOUNDCAPTUREBUFFER buffer;
- result = input->CreateCaptureBuffer( &bufferDescription, &buffer, NULL );
- if ( FAILED( result ) ) {
- input->Release();
- errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") creating input buffer (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Get the buffer size ... might be different from what we specified.
- DSCBCAPS dscbcaps;
- dscbcaps.dwSize = sizeof( DSCBCAPS );
- result = buffer->GetCaps( &dscbcaps );
- if ( FAILED( result ) ) {
- input->Release();
- buffer->Release();
- errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") getting buffer settings (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- dsBufferSize = dscbcaps.dwBufferBytes;
-
- // NOTE: We could have a problem here if this is a duplex stream
- // and the play and capture hardware buffer sizes are different
- // (I'm actually not sure if that is a problem or not).
- // Currently, we are not verifying that.
-
- // Lock the capture buffer
- LPVOID audioPtr;
- DWORD dataLen;
- result = buffer->Lock( 0, dsBufferSize, &audioPtr, &dataLen, NULL, NULL, 0 );
- if ( FAILED( result ) ) {
- input->Release();
- buffer->Release();
- errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") locking input buffer (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Zero the buffer
- ZeroMemory( audioPtr, dataLen );
-
- // Unlock the buffer
- result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
- if ( FAILED( result ) ) {
- input->Release();
- buffer->Release();
- errorStream_ << "RtApiDs::probeDeviceOpen: error (" << getErrorString( result ) << ") unlocking input buffer (" << dsDevices[ device ].name << ")!";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- ohandle = (void *) input;
- bhandle = (void *) buffer;
- }
-
- // Set various stream parameters
- DsHandle *handle = 0;
- stream_.nDeviceChannels[mode] = channels + firstChannel;
- stream_.nUserChannels[mode] = channels;
- stream_.bufferSize = *bufferSize;
- stream_.channelOffset[mode] = firstChannel;
- stream_.deviceInterleaved[mode] = true;
- if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
- else stream_.userInterleaved = true;
-
- // Set flag for buffer conversion
- stream_.doConvertBuffer[mode] = false;
- if (stream_.nUserChannels[mode] != stream_.nDeviceChannels[mode])
- stream_.doConvertBuffer[mode] = true;
- if (stream_.userFormat != stream_.deviceFormat[mode])
- stream_.doConvertBuffer[mode] = true;
- if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
- stream_.nUserChannels[mode] > 1 )
- stream_.doConvertBuffer[mode] = true;
-
- // Allocate necessary internal buffers
- long bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
- stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
- if ( stream_.userBuffer[mode] == NULL ) {
- errorText_ = "RtApiDs::probeDeviceOpen: error allocating user buffer memory.";
- goto error;
- }
-
- if ( stream_.doConvertBuffer[mode] ) {
-
- bool makeBuffer = true;
- bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
- if ( mode == INPUT ) {
- if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
- unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
- if ( bufferBytes <= (long) bytesOut ) makeBuffer = false;
- }
- }
-
- if ( makeBuffer ) {
- bufferBytes *= *bufferSize;
- if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
- stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
- if ( stream_.deviceBuffer == NULL ) {
- errorText_ = "RtApiDs::probeDeviceOpen: error allocating device buffer memory.";
- goto error;
- }
- }
- }
-
- // Allocate our DsHandle structures for the stream.
- if ( stream_.apiHandle == 0 ) {
- try {
- handle = new DsHandle;
- }
- catch ( std::bad_alloc& ) {
- errorText_ = "RtApiDs::probeDeviceOpen: error allocating AsioHandle memory.";
- goto error;
- }
-
- // Create a manual-reset event.
- handle->condition = CreateEvent( NULL, // no security
- TRUE, // manual-reset
- FALSE, // non-signaled initially
- NULL ); // unnamed
- stream_.apiHandle = (void *) handle;
- }
- else
- handle = (DsHandle *) stream_.apiHandle;
- handle->id[mode] = ohandle;
- handle->buffer[mode] = bhandle;
- handle->dsBufferSize[mode] = dsBufferSize;
- handle->dsPointerLeadTime[mode] = dsPointerLeadTime;
-
- stream_.device[mode] = device;
- stream_.state = STREAM_STOPPED;
- if ( stream_.mode == OUTPUT && mode == INPUT )
- // We had already set up an output stream.
- stream_.mode = DUPLEX;
- else
- stream_.mode = mode;
- stream_.nBuffers = nBuffers;
- stream_.sampleRate = sampleRate;
-
- // Setup the buffer conversion information structure.
- if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
-
- // Setup the callback thread.
- if ( stream_.callbackInfo.isRunning == false ) {
- unsigned threadId;
- stream_.callbackInfo.isRunning = true;
- stream_.callbackInfo.object = (void *) this;
- stream_.callbackInfo.thread = _beginthreadex( NULL, 0, &callbackHandler,
- &stream_.callbackInfo, 0, &threadId );
- if ( stream_.callbackInfo.thread == 0 ) {
- errorText_ = "RtApiDs::probeDeviceOpen: error creating callback thread!";
- goto error;
- }
-
- // Boost DS thread priority
- SetThreadPriority( (HANDLE) stream_.callbackInfo.thread, THREAD_PRIORITY_HIGHEST );
- }
- return SUCCESS;
-
- error:
- if ( handle ) {
- if ( handle->buffer[0] ) { // the object pointer can be NULL and valid
- LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];
- LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
- if ( buffer ) buffer->Release();
- object->Release();
- }
- if ( handle->buffer[1] ) {
- LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];
- LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
- if ( buffer ) buffer->Release();
- object->Release();
- }
- CloseHandle( handle->condition );
- delete handle;
- stream_.apiHandle = 0;
- }
-
- for ( int i=0; i<2; i++ ) {
- if ( stream_.userBuffer[i] ) {
- free( stream_.userBuffer[i] );
- stream_.userBuffer[i] = 0;
- }
- }
-
- if ( stream_.deviceBuffer ) {
- free( stream_.deviceBuffer );
- stream_.deviceBuffer = 0;
- }
-
- stream_.state = STREAM_CLOSED;
- return FAILURE;
-}
-
-void RtApiDs :: closeStream()
-{
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiDs::closeStream(): no open stream to close!";
- error( RtAudioError::WARNING );
- return;
- }
-
- // Stop the callback thread.
- stream_.callbackInfo.isRunning = false;
- WaitForSingleObject( (HANDLE) stream_.callbackInfo.thread, INFINITE );
- CloseHandle( (HANDLE) stream_.callbackInfo.thread );
-
- DsHandle *handle = (DsHandle *) stream_.apiHandle;
- if ( handle ) {
- if ( handle->buffer[0] ) { // the object pointer can be NULL and valid
- LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];
- LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
- if ( buffer ) {
- buffer->Stop();
- buffer->Release();
- }
- object->Release();
- }
- if ( handle->buffer[1] ) {
- LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1];
- LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
- if ( buffer ) {
- buffer->Stop();
- buffer->Release();
- }
- object->Release();
- }
- CloseHandle( handle->condition );
- delete handle;
- stream_.apiHandle = 0;
- }
-
- for ( int i=0; i<2; i++ ) {
- if ( stream_.userBuffer[i] ) {
- free( stream_.userBuffer[i] );
- stream_.userBuffer[i] = 0;
- }
- }
-
- if ( stream_.deviceBuffer ) {
- free( stream_.deviceBuffer );
- stream_.deviceBuffer = 0;
- }
-
- stream_.mode = UNINITIALIZED;
- stream_.state = STREAM_CLOSED;
-}
-
-void RtApiDs :: startStream()
-{
- verifyStream();
- if ( stream_.state == STREAM_RUNNING ) {
- errorText_ = "RtApiDs::startStream(): the stream is already running!";
- error( RtAudioError::WARNING );
- return;
- }
-
- #if defined( HAVE_GETTIMEOFDAY )
- gettimeofday( &stream_.lastTickTimestamp, NULL );
- #endif
-
- DsHandle *handle = (DsHandle *) stream_.apiHandle;
-
- // Increase scheduler frequency on lesser windows (a side-effect of
- // increasing timer accuracy). On greater windows (Win2K or later),
- // this is already in effect.
- timeBeginPeriod( 1 );
-
- buffersRolling = false;
- duplexPrerollBytes = 0;
-
- if ( stream_.mode == DUPLEX ) {
- // 0.5 seconds of silence in DUPLEX mode while the devices spin up and synchronize.
- duplexPrerollBytes = (int) ( 0.5 * stream_.sampleRate * formatBytes( stream_.deviceFormat[1] ) * stream_.nDeviceChannels[1] );
- }
-
- HRESULT result = 0;
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
-
- LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
- result = buffer->Play( 0, 0, DSBPLAY_LOOPING );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting output buffer!";
- errorText_ = errorStream_.str();
- goto unlock;
- }
- }
-
- if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
-
- LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
- result = buffer->Start( DSCBSTART_LOOPING );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::startStream: error (" << getErrorString( result ) << ") starting input buffer!";
- errorText_ = errorStream_.str();
- goto unlock;
- }
- }
-
- handle->drainCounter = 0;
- handle->internalDrain = false;
- ResetEvent( handle->condition );
- stream_.state = STREAM_RUNNING;
-
- unlock:
- if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );
-}
-
-void RtApiDs :: stopStream()
-{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiDs::stopStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
- }
-
- HRESULT result = 0;
- LPVOID audioPtr;
- DWORD dataLen;
- DsHandle *handle = (DsHandle *) stream_.apiHandle;
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
- if ( handle->drainCounter == 0 ) {
- handle->drainCounter = 2;
- WaitForSingleObject( handle->condition, INFINITE ); // block until signaled
- }
-
- stream_.state = STREAM_STOPPED;
-
- MUTEX_LOCK( &stream_.mutex );
-
- // Stop the buffer and clear memory
- LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
- result = buffer->Stop();
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping output buffer!";
- errorText_ = errorStream_.str();
- goto unlock;
- }
-
- // Lock the buffer and clear it so that if we start to play again,
- // we won't have old data playing.
- result = buffer->Lock( 0, handle->dsBufferSize[0], &audioPtr, &dataLen, NULL, NULL, 0 );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking output buffer!";
- errorText_ = errorStream_.str();
- goto unlock;
- }
-
- // Zero the DS buffer
- ZeroMemory( audioPtr, dataLen );
-
- // Unlock the DS buffer
- result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking output buffer!";
- errorText_ = errorStream_.str();
- goto unlock;
- }
-
- // If we start playing again, we must begin at beginning of buffer.
- handle->bufferPointer[0] = 0;
- }
-
- if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
- LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
- audioPtr = NULL;
- dataLen = 0;
-
- stream_.state = STREAM_STOPPED;
-
- if ( stream_.mode != DUPLEX )
- MUTEX_LOCK( &stream_.mutex );
-
- result = buffer->Stop();
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") stopping input buffer!";
- errorText_ = errorStream_.str();
- goto unlock;
- }
-
- // Lock the buffer and clear it so that if we start to play again,
- // we won't have old data playing.
- result = buffer->Lock( 0, handle->dsBufferSize[1], &audioPtr, &dataLen, NULL, NULL, 0 );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") locking input buffer!";
- errorText_ = errorStream_.str();
- goto unlock;
- }
-
- // Zero the DS buffer
- ZeroMemory( audioPtr, dataLen );
-
- // Unlock the DS buffer
- result = buffer->Unlock( audioPtr, dataLen, NULL, 0 );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::stopStream: error (" << getErrorString( result ) << ") unlocking input buffer!";
- errorText_ = errorStream_.str();
- goto unlock;
- }
-
- // If we start recording again, we must begin at beginning of buffer.
- handle->bufferPointer[1] = 0;
- }
-
- unlock:
- timeEndPeriod( 1 ); // revert to normal scheduler frequency on lesser windows.
- MUTEX_UNLOCK( &stream_.mutex );
-
- if ( FAILED( result ) ) error( RtAudioError::SYSTEM_ERROR );
-}
-
-void RtApiDs :: abortStream()
-{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiDs::abortStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
- }
-
- DsHandle *handle = (DsHandle *) stream_.apiHandle;
- handle->drainCounter = 2;
-
- stopStream();
-}
-
-void RtApiDs :: callbackEvent()
-{
- if ( stream_.state == STREAM_STOPPED || stream_.state == STREAM_STOPPING ) {
- Sleep( 50 ); // sleep 50 milliseconds
- return;
- }
-
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiDs::callbackEvent(): the stream is closed ... this shouldn't happen!";
- error( RtAudioError::WARNING );
- return;
- }
-
- CallbackInfo *info = (CallbackInfo *) &stream_.callbackInfo;
- DsHandle *handle = (DsHandle *) stream_.apiHandle;
-
- // Check if we were draining the stream and signal is finished.
- if ( handle->drainCounter > stream_.nBuffers + 2 ) {
-
- stream_.state = STREAM_STOPPING;
- if ( handle->internalDrain == false )
- SetEvent( handle->condition );
- else
- stopStream();
- return;
- }
-
- // Invoke user callback to get fresh output data UNLESS we are
- // draining stream.
- if ( handle->drainCounter == 0 ) {
- RtAudioCallback callback = (RtAudioCallback) info->callback;
- double streamTime = getStreamTime();
- RtAudioStreamStatus status = 0;
- if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
- status |= RTAUDIO_OUTPUT_UNDERFLOW;
- handle->xrun[0] = false;
- }
- if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
- status |= RTAUDIO_INPUT_OVERFLOW;
- handle->xrun[1] = false;
- }
- int cbReturnValue = callback( stream_.userBuffer[0], stream_.userBuffer[1],
- stream_.bufferSize, streamTime, status, info->userData );
- if ( cbReturnValue == 2 ) {
- stream_.state = STREAM_STOPPING;
- handle->drainCounter = 2;
- abortStream();
- return;
- }
- else if ( cbReturnValue == 1 ) {
- handle->drainCounter = 1;
- handle->internalDrain = true;
- }
- }
-
- HRESULT result;
- DWORD currentWritePointer, safeWritePointer;
- DWORD currentReadPointer, safeReadPointer;
- UINT nextWritePointer;
-
- LPVOID buffer1 = NULL;
- LPVOID buffer2 = NULL;
- DWORD bufferSize1 = 0;
- DWORD bufferSize2 = 0;
-
- char *buffer;
- long bufferBytes;
-
- MUTEX_LOCK( &stream_.mutex );
- if ( stream_.state == STREAM_STOPPED ) {
- MUTEX_UNLOCK( &stream_.mutex );
- return;
- }
-
- if ( buffersRolling == false ) {
- if ( stream_.mode == DUPLEX ) {
- //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
-
- // It takes a while for the devices to get rolling. As a result,
- // there's no guarantee that the capture and write device pointers
- // will move in lockstep. Wait here for both devices to start
- // rolling, and then set our buffer pointers accordingly.
- // e.g. Crystal Drivers: the capture buffer starts up 5700 to 9600
- // bytes later than the write buffer.
-
- // Stub: a serious risk of having a pre-emptive scheduling round
- // take place between the two GetCurrentPosition calls... but I'm
- // really not sure how to solve the problem. Temporarily boost to
- // Realtime priority, maybe; but I'm not sure what priority the
- // DirectSound service threads run at. We *should* be roughly
- // within a ms or so of correct.
-
- LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
- LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
-
- DWORD startSafeWritePointer, startSafeReadPointer;
-
- result = dsWriteBuffer->GetCurrentPosition( NULL, &startSafeWritePointer );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
- errorText_ = errorStream_.str();
- MUTEX_UNLOCK( &stream_.mutex );
- error( RtAudioError::SYSTEM_ERROR );
- return;
- }
- result = dsCaptureBuffer->GetCurrentPosition( NULL, &startSafeReadPointer );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
- errorText_ = errorStream_.str();
- MUTEX_UNLOCK( &stream_.mutex );
- error( RtAudioError::SYSTEM_ERROR );
- return;
- }
- while ( true ) {
- result = dsWriteBuffer->GetCurrentPosition( NULL, &safeWritePointer );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
- errorText_ = errorStream_.str();
- MUTEX_UNLOCK( &stream_.mutex );
- error( RtAudioError::SYSTEM_ERROR );
- return;
- }
- result = dsCaptureBuffer->GetCurrentPosition( NULL, &safeReadPointer );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
- errorText_ = errorStream_.str();
- MUTEX_UNLOCK( &stream_.mutex );
- error( RtAudioError::SYSTEM_ERROR );
- return;
- }
- if ( safeWritePointer != startSafeWritePointer && safeReadPointer != startSafeReadPointer ) break;
- Sleep( 1 );
- }
-
- //assert( handle->dsBufferSize[0] == handle->dsBufferSize[1] );
-
- handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];
- if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];
- handle->bufferPointer[1] = safeReadPointer;
- }
- else if ( stream_.mode == OUTPUT ) {
-
- // Set the proper nextWritePosition after initial startup.
- LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
- result = dsWriteBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
- errorText_ = errorStream_.str();
- MUTEX_UNLOCK( &stream_.mutex );
- error( RtAudioError::SYSTEM_ERROR );
- return;
- }
- handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0];
- if ( handle->bufferPointer[0] >= handle->dsBufferSize[0] ) handle->bufferPointer[0] -= handle->dsBufferSize[0];
- }
-
- buffersRolling = true;
- }
-
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
-
- LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
-
- if ( handle->drainCounter > 1 ) { // write zeros to the output stream
- bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];
- bufferBytes *= formatBytes( stream_.userFormat );
- memset( stream_.userBuffer[0], 0, bufferBytes );
- }
-
- // Setup parameters and do buffer conversion if necessary.
- if ( stream_.doConvertBuffer[0] ) {
- buffer = stream_.deviceBuffer;
- convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
- bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[0];
- bufferBytes *= formatBytes( stream_.deviceFormat[0] );
- }
- else {
- buffer = stream_.userBuffer[0];
- bufferBytes = stream_.bufferSize * stream_.nUserChannels[0];
- bufferBytes *= formatBytes( stream_.userFormat );
- }
-
- // No byte swapping necessary in DirectSound implementation.
-
- // Ahhh ... windoze. 16-bit data is signed but 8-bit data is
- // unsigned. So, we need to convert our signed 8-bit data here to
- // unsigned.
- if ( stream_.deviceFormat[0] == RTAUDIO_SINT8 )
- for ( int i=0; i<bufferBytes; i++ ) buffer[i] = (unsigned char) ( buffer[i] + 128 );
-
- DWORD dsBufferSize = handle->dsBufferSize[0];
- nextWritePointer = handle->bufferPointer[0];
-
- DWORD endWrite, leadPointer;
- while ( true ) {
- // Find out where the read and "safe write" pointers are.
- result = dsBuffer->GetCurrentPosition( ¤tWritePointer, &safeWritePointer );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
- errorText_ = errorStream_.str();
- MUTEX_UNLOCK( &stream_.mutex );
- error( RtAudioError::SYSTEM_ERROR );
- return;
- }
-
- // We will copy our output buffer into the region between
- // safeWritePointer and leadPointer. If leadPointer is not
- // beyond the next endWrite position, wait until it is.
- leadPointer = safeWritePointer + handle->dsPointerLeadTime[0];
- //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl;
- if ( leadPointer > dsBufferSize ) leadPointer -= dsBufferSize;
- if ( leadPointer < nextWritePointer ) leadPointer += dsBufferSize; // unwrap offset
- endWrite = nextWritePointer + bufferBytes;
-
- // Check whether the entire write region is behind the play pointer.
- if ( leadPointer >= endWrite ) break;
-
- // If we are here, then we must wait until the leadPointer advances
- // beyond the end of our next write region. We use the
- // Sleep() function to suspend operation until that happens.
- double millis = ( endWrite - leadPointer ) * 1000.0;
- millis /= ( formatBytes( stream_.deviceFormat[0]) * stream_.nDeviceChannels[0] * stream_.sampleRate);
- if ( millis < 1.0 ) millis = 1.0;
- Sleep( (DWORD) millis );
- }
-
- if ( dsPointerBetween( nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize )
- || dsPointerBetween( endWrite, safeWritePointer, currentWritePointer, dsBufferSize ) ) {
- // We've strayed into the forbidden zone ... resync the read pointer.
- handle->xrun[0] = true;
- nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes;
- if ( nextWritePointer >= dsBufferSize ) nextWritePointer -= dsBufferSize;
- handle->bufferPointer[0] = nextWritePointer;
- endWrite = nextWritePointer + bufferBytes;
- }
-
- // Lock free space in the buffer
- result = dsBuffer->Lock( nextWritePointer, bufferBytes, &buffer1,
- &bufferSize1, &buffer2, &bufferSize2, 0 );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";
- errorText_ = errorStream_.str();
- MUTEX_UNLOCK( &stream_.mutex );
- error( RtAudioError::SYSTEM_ERROR );
- return;
- }
-
- // Copy our buffer into the DS buffer
- CopyMemory( buffer1, buffer, bufferSize1 );
- if ( buffer2 != NULL ) CopyMemory( buffer2, buffer+bufferSize1, bufferSize2 );
-
- // Update our buffer offset and unlock sound buffer
- dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";
- errorText_ = errorStream_.str();
- MUTEX_UNLOCK( &stream_.mutex );
- error( RtAudioError::SYSTEM_ERROR );
- return;
- }
- nextWritePointer = ( nextWritePointer + bufferSize1 + bufferSize2 ) % dsBufferSize;
- handle->bufferPointer[0] = nextWritePointer;
- }
-
- // Don't bother draining input
- if ( handle->drainCounter ) {
- handle->drainCounter++;
- goto unlock;
- }
-
- if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
-
- // Setup parameters.
- if ( stream_.doConvertBuffer[1] ) {
- buffer = stream_.deviceBuffer;
- bufferBytes = stream_.bufferSize * stream_.nDeviceChannels[1];
- bufferBytes *= formatBytes( stream_.deviceFormat[1] );
- }
- else {
- buffer = stream_.userBuffer[1];
- bufferBytes = stream_.bufferSize * stream_.nUserChannels[1];
- bufferBytes *= formatBytes( stream_.userFormat );
- }
-
- LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
- long nextReadPointer = handle->bufferPointer[1];
- DWORD dsBufferSize = handle->dsBufferSize[1];
-
- // Find out where the write and "safe read" pointers are.
- result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
- errorText_ = errorStream_.str();
- MUTEX_UNLOCK( &stream_.mutex );
- error( RtAudioError::SYSTEM_ERROR );
- return;
- }
-
- if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset
- DWORD endRead = nextReadPointer + bufferBytes;
-
- // Handling depends on whether we are INPUT or DUPLEX.
- // If we're in INPUT mode then waiting is a good thing. If we're in DUPLEX mode,
- // then a wait here will drag the write pointers into the forbidden zone.
- //
- // In DUPLEX mode, rather than wait, we will back off the read pointer until
- // it's in a safe position. This causes dropouts, but it seems to be the only
- // practical way to sync up the read and write pointers reliably, given the
- // the very complex relationship between phase and increment of the read and write
- // pointers.
- //
- // In order to minimize audible dropouts in DUPLEX mode, we will
- // provide a pre-roll period of 0.5 seconds in which we return
- // zeros from the read buffer while the pointers sync up.
-
- if ( stream_.mode == DUPLEX ) {
- if ( safeReadPointer < endRead ) {
- if ( duplexPrerollBytes <= 0 ) {
- // Pre-roll time over. Be more agressive.
- int adjustment = endRead-safeReadPointer;
-
- handle->xrun[1] = true;
- // Two cases:
- // - large adjustments: we've probably run out of CPU cycles, so just resync exactly,
- // and perform fine adjustments later.
- // - small adjustments: back off by twice as much.
- if ( adjustment >= 2*bufferBytes )
- nextReadPointer = safeReadPointer-2*bufferBytes;
- else
- nextReadPointer = safeReadPointer-bufferBytes-adjustment;
-
- if ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;
-
- }
- else {
- // In pre=roll time. Just do it.
- nextReadPointer = safeReadPointer - bufferBytes;
- while ( nextReadPointer < 0 ) nextReadPointer += dsBufferSize;
- }
- endRead = nextReadPointer + bufferBytes;
- }
- }
- else { // mode == INPUT
- while ( safeReadPointer < endRead && stream_.callbackInfo.isRunning ) {
- // See comments for playback.
- double millis = (endRead - safeReadPointer) * 1000.0;
- millis /= ( formatBytes(stream_.deviceFormat[1]) * stream_.nDeviceChannels[1] * stream_.sampleRate);
- if ( millis < 1.0 ) millis = 1.0;
- Sleep( (DWORD) millis );
-
- // Wake up and find out where we are now.
- result = dsBuffer->GetCurrentPosition( ¤tReadPointer, &safeReadPointer );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
- errorText_ = errorStream_.str();
- MUTEX_UNLOCK( &stream_.mutex );
- error( RtAudioError::SYSTEM_ERROR );
- return;
- }
-
- if ( safeReadPointer < (DWORD)nextReadPointer ) safeReadPointer += dsBufferSize; // unwrap offset
- }
- }
-
- // Lock free space in the buffer
- result = dsBuffer->Lock( nextReadPointer, bufferBytes, &buffer1,
- &bufferSize1, &buffer2, &bufferSize2, 0 );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";
- errorText_ = errorStream_.str();
- MUTEX_UNLOCK( &stream_.mutex );
- error( RtAudioError::SYSTEM_ERROR );
- return;
- }
-
- if ( duplexPrerollBytes <= 0 ) {
- // Copy our buffer into the DS buffer
- CopyMemory( buffer, buffer1, bufferSize1 );
- if ( buffer2 != NULL ) CopyMemory( buffer+bufferSize1, buffer2, bufferSize2 );
- }
- else {
- memset( buffer, 0, bufferSize1 );
- if ( buffer2 != NULL ) memset( buffer + bufferSize1, 0, bufferSize2 );
- duplexPrerollBytes -= bufferSize1 + bufferSize2;
- }
-
- // Update our buffer offset and unlock sound buffer
- nextReadPointer = ( nextReadPointer + bufferSize1 + bufferSize2 ) % dsBufferSize;
- dsBuffer->Unlock( buffer1, bufferSize1, buffer2, bufferSize2 );
- if ( FAILED( result ) ) {
- errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";
- errorText_ = errorStream_.str();
- MUTEX_UNLOCK( &stream_.mutex );
- error( RtAudioError::SYSTEM_ERROR );
- return;
- }
- handle->bufferPointer[1] = nextReadPointer;
-
- // No byte swapping necessary in DirectSound implementation.
-
- // If necessary, convert 8-bit data from unsigned to signed.
- if ( stream_.deviceFormat[1] == RTAUDIO_SINT8 )
- for ( int j=0; j<bufferBytes; j++ ) buffer[j] = (signed char) ( buffer[j] - 128 );
-
- // Do buffer conversion if necessary.
- if ( stream_.doConvertBuffer[1] )
- convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
- }
-
- unlock:
- MUTEX_UNLOCK( &stream_.mutex );
- RtApi::tickStreamTime();
-}
-
-// Definitions for utility functions and callbacks
-// specific to the DirectSound implementation.
-
-static unsigned __stdcall callbackHandler( void *ptr )
-{
- CallbackInfo *info = (CallbackInfo *) ptr;
- RtApiDs *object = (RtApiDs *) info->object;
- bool* isRunning = &info->isRunning;
-
- while ( *isRunning == true ) {
- object->callbackEvent();
- }
-
- _endthreadex( 0 );
- return 0;
-}
-
-static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
- LPCTSTR description,
- LPCTSTR /*module*/,
- LPVOID lpContext )
-{
- struct DsProbeData& probeInfo = *(struct DsProbeData*) lpContext;
- std::vector<struct DsDevice>& dsDevices = *probeInfo.dsDevices;
-
- HRESULT hr;
- bool validDevice = false;
- if ( probeInfo.isInput == true ) {
- DSCCAPS caps;
- LPDIRECTSOUNDCAPTURE object;
-
- hr = DirectSoundCaptureCreate( lpguid, &object, NULL );
- if ( hr != DS_OK ) return TRUE;
-
- caps.dwSize = sizeof(caps);
- hr = object->GetCaps( &caps );
- if ( hr == DS_OK ) {
- if ( caps.dwChannels > 0 && caps.dwFormats > 0 )
- validDevice = true;
- }
- object->Release();
- }
- else {
- DSCAPS caps;
- LPDIRECTSOUND object;
- hr = DirectSoundCreate( lpguid, &object, NULL );
- if ( hr != DS_OK ) return TRUE;
-
- caps.dwSize = sizeof(caps);
- hr = object->GetCaps( &caps );
- if ( hr == DS_OK ) {
- if ( caps.dwFlags & DSCAPS_PRIMARYMONO || caps.dwFlags & DSCAPS_PRIMARYSTEREO )
- validDevice = true;
- }
- object->Release();
- }
-
- // If good device, then save its name and guid.
- std::string name = convertCharPointerToStdString( description );
- //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )
- if ( lpguid == NULL )
- name = "Default Device";
- if ( validDevice ) {
- for ( unsigned int i=0; i<dsDevices.size(); i++ ) {
- if ( dsDevices[i].name == name ) {
- dsDevices[i].found = true;
- if ( probeInfo.isInput ) {
- dsDevices[i].id[1] = lpguid;
- dsDevices[i].validId[1] = true;
- }
- else {
- dsDevices[i].id[0] = lpguid;
- dsDevices[i].validId[0] = true;
- }
- return TRUE;
- }
- }
-
- DsDevice device;
- device.name = name;
- device.found = true;
- if ( probeInfo.isInput ) {
- device.id[1] = lpguid;
- device.validId[1] = true;
- }
- else {
- device.id[0] = lpguid;
- device.validId[0] = true;
- }
- dsDevices.push_back( device );
- }
-
- return TRUE;
-}
-
-static const char* getErrorString( int code )
-{
- switch ( code ) {
-
- case DSERR_ALLOCATED:
- return "Already allocated";
-
- case DSERR_CONTROLUNAVAIL:
- return "Control unavailable";
-
- case DSERR_INVALIDPARAM:
- return "Invalid parameter";
-
- case DSERR_INVALIDCALL:
- return "Invalid call";
-
- case DSERR_GENERIC:
- return "Generic error";
-
- case DSERR_PRIOLEVELNEEDED:
- return "Priority level needed";
-
- case DSERR_OUTOFMEMORY:
- return "Out of memory";
-
- case DSERR_BADFORMAT:
- return "The sample rate or the channel format is not supported";
-
- case DSERR_UNSUPPORTED:
- return "Not supported";
-
- case DSERR_NODRIVER:
- return "No driver";
-
- case DSERR_ALREADYINITIALIZED:
- return "Already initialized";
-
- case DSERR_NOAGGREGATION:
- return "No aggregation";
-
- case DSERR_BUFFERLOST:
- return "Buffer lost";
-
- case DSERR_OTHERAPPHASPRIO:
- return "Another application already has priority";
-
- case DSERR_UNINITIALIZED:
- return "Uninitialized";
-
- default:
- return "DirectSound unknown error";
- }
-}
-//******************** End of __WINDOWS_DS__ *********************//
-#endif
-
-
-#if defined(__LINUX_ALSA__)
-
-#include <alsa/asoundlib.h>
-#include <unistd.h>
-
- // A structure to hold various information related to the ALSA API
- // implementation.
-struct AlsaHandle {
- snd_pcm_t *handles[2];
- bool synchronized;
- bool xrun[2];
- pthread_cond_t runnable_cv;
- bool runnable;
-
- AlsaHandle()
- :synchronized(false), runnable(false) { xrun[0] = false; xrun[1] = false; }
-};
-
-static void *alsaCallbackHandler( void * ptr );
-
-RtApiAlsa :: RtApiAlsa()
-{
- // Nothing to do here.
-}
-
-RtApiAlsa :: ~RtApiAlsa()
-{
- if ( stream_.state != STREAM_CLOSED ) closeStream();
-}
-
-unsigned int RtApiAlsa :: getDeviceCount( void )
-{
- unsigned nDevices = 0;
- int result, subdevice, card;
- char name[64];
- snd_ctl_t *handle = 0;
-
- // Count cards and devices
- card = -1;
- snd_card_next( &card );
- while ( card >= 0 ) {
- sprintf( name, "hw:%d", card );
- result = snd_ctl_open( &handle, name, 0 );
- if ( result < 0 ) {
- handle = 0;
- errorStream_ << "RtApiAlsa::getDeviceCount: control open, card = " << card << ", " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- goto nextcard;
- }
- subdevice = -1;
- while( 1 ) {
- result = snd_ctl_pcm_next_device( handle, &subdevice );
- if ( result < 0 ) {
- errorStream_ << "RtApiAlsa::getDeviceCount: control next device, card = " << card << ", " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- break;
- }
- if ( subdevice < 0 )
- break;
- nDevices++;
- }
- nextcard:
- if ( handle )
- snd_ctl_close( handle );
- snd_card_next( &card );
- }
-
- result = snd_ctl_open( &handle, "default", 0 );
- if (result == 0) {
- nDevices++;
- snd_ctl_close( handle );
- }
-
- return nDevices;
-}
-
-RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
-{
- RtAudio::DeviceInfo info;
- info.probed = false;
-
- unsigned nDevices = 0;
- int result, subdevice, card;
- char name[64];
- snd_ctl_t *chandle = 0;
-
- // Count cards and devices
- card = -1;
- subdevice = -1;
- snd_card_next( &card );
- while ( card >= 0 ) {
- sprintf( name, "hw:%d", card );
- result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );
- if ( result < 0 ) {
- chandle = 0;
- errorStream_ << "RtApiAlsa::getDeviceInfo: control open, card = " << card << ", " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- goto nextcard;
- }
- subdevice = -1;
- while( 1 ) {
- result = snd_ctl_pcm_next_device( chandle, &subdevice );
- if ( result < 0 ) {
- errorStream_ << "RtApiAlsa::getDeviceInfo: control next device, card = " << card << ", " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- break;
- }
- if ( subdevice < 0 ) break;
- if ( nDevices == device ) {
- sprintf( name, "hw:%d,%d", card, subdevice );
- goto foundDevice;
- }
- nDevices++;
- }
- nextcard:
- if ( chandle )
- snd_ctl_close( chandle );
- snd_card_next( &card );
- }
-
- result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );
- if ( result == 0 ) {
- if ( nDevices == device ) {
- strcpy( name, "default" );
- goto foundDevice;
- }
- nDevices++;
- }
-
- if ( nDevices == 0 ) {
- errorText_ = "RtApiAlsa::getDeviceInfo: no devices found!";
- error( RtAudioError::INVALID_USE );
- return info;
- }
-
- if ( device >= nDevices ) {
- errorText_ = "RtApiAlsa::getDeviceInfo: device ID is invalid!";
- error( RtAudioError::INVALID_USE );
- return info;
- }
-
- foundDevice:
-
- // If a stream is already open, we cannot probe the stream devices.
- // Thus, use the saved results.
- if ( stream_.state != STREAM_CLOSED &&
- ( stream_.device[0] == device || stream_.device[1] == device ) ) {
- snd_ctl_close( chandle );
- if ( device >= devices_.size() ) {
- errorText_ = "RtApiAlsa::getDeviceInfo: device ID was not present before stream was opened.";
- error( RtAudioError::WARNING );
- return info;
- }
- return devices_[ device ];
- }
-
- int openMode = SND_PCM_ASYNC;
- snd_pcm_stream_t stream;
- snd_pcm_info_t *pcminfo;
- snd_pcm_info_alloca( &pcminfo );
- snd_pcm_t *phandle;
- snd_pcm_hw_params_t *params;
- snd_pcm_hw_params_alloca( ¶ms );
-
- // First try for playback unless default device (which has subdev -1)
- stream = SND_PCM_STREAM_PLAYBACK;
- snd_pcm_info_set_stream( pcminfo, stream );
- if ( subdevice != -1 ) {
- snd_pcm_info_set_device( pcminfo, subdevice );
- snd_pcm_info_set_subdevice( pcminfo, 0 );
-
- result = snd_ctl_pcm_info( chandle, pcminfo );
- if ( result < 0 ) {
- // Device probably doesn't support playback.
- goto captureProbe;
- }
- }
-
- result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK );
- if ( result < 0 ) {
- errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- goto captureProbe;
- }
-
- // The device is open ... fill the parameter structure.
- result = snd_pcm_hw_params_any( phandle, params );
- if ( result < 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- goto captureProbe;
- }
-
- // Get output channel information.
- unsigned int value;
- result = snd_pcm_hw_params_get_channels_max( params, &value );
- if ( result < 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") output channels, " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- goto captureProbe;
- }
- info.outputChannels = value;
- snd_pcm_close( phandle );
-
- captureProbe:
- stream = SND_PCM_STREAM_CAPTURE;
- snd_pcm_info_set_stream( pcminfo, stream );
-
- // Now try for capture unless default device (with subdev = -1)
- if ( subdevice != -1 ) {
- result = snd_ctl_pcm_info( chandle, pcminfo );
- snd_ctl_close( chandle );
- if ( result < 0 ) {
- // Device probably doesn't support capture.
- if ( info.outputChannels == 0 ) return info;
- goto probeParameters;
- }
- }
- else
- snd_ctl_close( chandle );
-
- result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);
- if ( result < 0 ) {
- errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- if ( info.outputChannels == 0 ) return info;
- goto probeParameters;
- }
-
- // The device is open ... fill the parameter structure.
- result = snd_pcm_hw_params_any( phandle, params );
- if ( result < 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- if ( info.outputChannels == 0 ) return info;
- goto probeParameters;
- }
-
- result = snd_pcm_hw_params_get_channels_max( params, &value );
- if ( result < 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::getDeviceInfo: error getting device (" << name << ") input channels, " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- if ( info.outputChannels == 0 ) return info;
- goto probeParameters;
- }
- info.inputChannels = value;
- snd_pcm_close( phandle );
-
- // If device opens for both playback and capture, we determine the channels.
- if ( info.outputChannels > 0 && info.inputChannels > 0 )
- info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
-
- // ALSA doesn't provide default devices so we'll use the first available one.
- if ( device == 0 && info.outputChannels > 0 )
- info.isDefaultOutput = true;
- if ( device == 0 && info.inputChannels > 0 )
- info.isDefaultInput = true;
-
- probeParameters:
- // At this point, we just need to figure out the supported data
- // formats and sample rates. We'll proceed by opening the device in
- // the direction with the maximum number of channels, or playback if
- // they are equal. This might limit our sample rate options, but so
- // be it.
-
- if ( info.outputChannels >= info.inputChannels )
- stream = SND_PCM_STREAM_PLAYBACK;
- else
- stream = SND_PCM_STREAM_CAPTURE;
- snd_pcm_info_set_stream( pcminfo, stream );
-
- result = snd_pcm_open( &phandle, name, stream, openMode | SND_PCM_NONBLOCK);
- if ( result < 0 ) {
- errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_open error for device (" << name << "), " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- // The device is open ... fill the parameter structure.
- result = snd_pcm_hw_params_any( phandle, params );
- if ( result < 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::getDeviceInfo: snd_pcm_hw_params error for device (" << name << "), " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- // Test our discrete set of sample rate values.
- info.sampleRates.clear();
- for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
- if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 ) {
- info.sampleRates.push_back( SAMPLE_RATES[i] );
-
- if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )
- info.preferredSampleRate = SAMPLE_RATES[i];
- }
- }
- if ( info.sampleRates.size() == 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::getDeviceInfo: no supported sample rates found for device (" << name << ").";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- // Probe the supported data formats ... we don't care about endian-ness just yet
- snd_pcm_format_t format;
- info.nativeFormats = 0;
- format = SND_PCM_FORMAT_S8;
- if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
- info.nativeFormats |= RTAUDIO_SINT8;
- format = SND_PCM_FORMAT_S16;
- if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
- info.nativeFormats |= RTAUDIO_SINT16;
- format = SND_PCM_FORMAT_S24;
- if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
- info.nativeFormats |= RTAUDIO_SINT24;
- format = SND_PCM_FORMAT_S32;
- if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
- info.nativeFormats |= RTAUDIO_SINT32;
- format = SND_PCM_FORMAT_FLOAT;
- if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
- info.nativeFormats |= RTAUDIO_FLOAT32;
- format = SND_PCM_FORMAT_FLOAT64;
- if ( snd_pcm_hw_params_test_format( phandle, params, format ) == 0 )
- info.nativeFormats |= RTAUDIO_FLOAT64;
-
- // Check that we have at least one supported format
- if ( info.nativeFormats == 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::getDeviceInfo: pcm device (" << name << ") data format not supported by RtAudio.";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- // Get the device name
- char *cardname;
- result = snd_card_get_name( card, &cardname );
- if ( result >= 0 ) {
- sprintf( name, "hw:%s,%d", cardname, subdevice );
- free( cardname );
- }
- info.name = name;
-
- // That's all ... close the device and return
- snd_pcm_close( phandle );
- info.probed = true;
- return info;
-}
-
-void RtApiAlsa :: saveDeviceInfo( void )
-{
- devices_.clear();
-
- unsigned int nDevices = getDeviceCount();
- devices_.resize( nDevices );
- for ( unsigned int i=0; i<nDevices; i++ )
- devices_[i] = getDeviceInfo( i );
-}
-
-bool RtApiAlsa :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
- RtAudioFormat format, unsigned int *bufferSize,
- RtAudio::StreamOptions *options )
-
-{
-#if defined(__RTAUDIO_DEBUG__)
- snd_output_t *out;
- snd_output_stdio_attach(&out, stderr, 0);
-#endif
-
- // I'm not using the "plug" interface ... too much inconsistent behavior.
-
- unsigned nDevices = 0;
- int result, subdevice, card;
- char name[64];
- snd_ctl_t *chandle;
-
- if ( options && options->flags & RTAUDIO_ALSA_USE_DEFAULT )
- snprintf(name, sizeof(name), "%s", "default");
- else {
- // Count cards and devices
- card = -1;
- snd_card_next( &card );
- while ( card >= 0 ) {
- sprintf( name, "hw:%d", card );
- result = snd_ctl_open( &chandle, name, SND_CTL_NONBLOCK );
- if ( result < 0 ) {
- errorStream_ << "RtApiAlsa::probeDeviceOpen: control open, card = " << card << ", " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- subdevice = -1;
- while( 1 ) {
- result = snd_ctl_pcm_next_device( chandle, &subdevice );
- if ( result < 0 ) break;
- if ( subdevice < 0 ) break;
- if ( nDevices == device ) {
- sprintf( name, "hw:%d,%d", card, subdevice );
- snd_ctl_close( chandle );
- goto foundDevice;
- }
- nDevices++;
- }
- snd_ctl_close( chandle );
- snd_card_next( &card );
- }
-
- result = snd_ctl_open( &chandle, "default", SND_CTL_NONBLOCK );
- if ( result == 0 ) {
- if ( nDevices == device ) {
- strcpy( name, "default" );
- snd_ctl_close( chandle );
- goto foundDevice;
- }
- nDevices++;
- }
- snd_ctl_close( chandle );
-
- if ( nDevices == 0 ) {
- // This should not happen because a check is made before this function is called.
- errorText_ = "RtApiAlsa::probeDeviceOpen: no devices found!";
- return FAILURE;
- }
-
- if ( device >= nDevices ) {
- // This should not happen because a check is made before this function is called.
- errorText_ = "RtApiAlsa::probeDeviceOpen: device ID is invalid!";
- return FAILURE;
- }
- }
-
- foundDevice:
-
- // The getDeviceInfo() function will not work for a device that is
- // already open. Thus, we'll probe the system before opening a
- // stream and save the results for use by getDeviceInfo().
- if ( mode == OUTPUT || ( mode == INPUT && stream_.mode != OUTPUT ) ) // only do once
- this->saveDeviceInfo();
-
- snd_pcm_stream_t stream;
- if ( mode == OUTPUT )
- stream = SND_PCM_STREAM_PLAYBACK;
- else
- stream = SND_PCM_STREAM_CAPTURE;
-
- snd_pcm_t *phandle;
- int openMode = SND_PCM_ASYNC;
- result = snd_pcm_open( &phandle, name, stream, openMode );
- if ( result < 0 ) {
- if ( mode == OUTPUT )
- errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for output.";
- else
- errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device (" << name << ") won't open for input.";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Fill the parameter structure.
- snd_pcm_hw_params_t *hw_params;
- snd_pcm_hw_params_alloca( &hw_params );
- result = snd_pcm_hw_params_any( phandle, hw_params );
- if ( result < 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") parameters, " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
-#if defined(__RTAUDIO_DEBUG__)
- fprintf( stderr, "\nRtApiAlsa: dump hardware params just after device open:\n\n" );
- snd_pcm_hw_params_dump( hw_params, out );
-#endif
-
- // Set access ... check user preference.
- if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) {
- stream_.userInterleaved = false;
- result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );
- if ( result < 0 ) {
- result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );
- stream_.deviceInterleaved[mode] = true;
- }
- else
- stream_.deviceInterleaved[mode] = false;
- }
- else {
- stream_.userInterleaved = true;
- result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED );
- if ( result < 0 ) {
- result = snd_pcm_hw_params_set_access( phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED );
- stream_.deviceInterleaved[mode] = false;
- }
- else
- stream_.deviceInterleaved[mode] = true;
- }
-
- if ( result < 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") access, " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Determine how to set the device format.
- stream_.userFormat = format;
- snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN;
-
- if ( format == RTAUDIO_SINT8 )
- deviceFormat = SND_PCM_FORMAT_S8;
- else if ( format == RTAUDIO_SINT16 )
- deviceFormat = SND_PCM_FORMAT_S16;
- else if ( format == RTAUDIO_SINT24 )
- deviceFormat = SND_PCM_FORMAT_S24;
- else if ( format == RTAUDIO_SINT32 )
- deviceFormat = SND_PCM_FORMAT_S32;
- else if ( format == RTAUDIO_FLOAT32 )
- deviceFormat = SND_PCM_FORMAT_FLOAT;
- else if ( format == RTAUDIO_FLOAT64 )
- deviceFormat = SND_PCM_FORMAT_FLOAT64;
-
- if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) {
- stream_.deviceFormat[mode] = format;
- goto setFormat;
- }
-
- // The user requested format is not natively supported by the device.
- deviceFormat = SND_PCM_FORMAT_FLOAT64;
- if ( snd_pcm_hw_params_test_format( phandle, hw_params, deviceFormat ) == 0 ) {
- stream_.deviceFormat[mode] = RTAUDIO_FLOAT64;
- goto setFormat;
- }
-
- deviceFormat = SND_PCM_FORMAT_FLOAT;
- if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
- stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
- goto setFormat;
- }
-
- deviceFormat = SND_PCM_FORMAT_S32;
- if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
- stream_.deviceFormat[mode] = RTAUDIO_SINT32;
- goto setFormat;
- }
-
- deviceFormat = SND_PCM_FORMAT_S24;
- if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
- stream_.deviceFormat[mode] = RTAUDIO_SINT24;
- goto setFormat;
- }
-
- deviceFormat = SND_PCM_FORMAT_S16;
- if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
- stream_.deviceFormat[mode] = RTAUDIO_SINT16;
- goto setFormat;
- }
-
- deviceFormat = SND_PCM_FORMAT_S8;
- if ( snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat ) == 0 ) {
- stream_.deviceFormat[mode] = RTAUDIO_SINT8;
- goto setFormat;
- }
-
- // If we get here, no supported format was found.
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::probeDeviceOpen: pcm device " << device << " data format not supported by RtAudio.";
- errorText_ = errorStream_.str();
- return FAILURE;
-
- setFormat:
- result = snd_pcm_hw_params_set_format( phandle, hw_params, deviceFormat );
- if ( result < 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting pcm device (" << name << ") data format, " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Determine whether byte-swaping is necessary.
- stream_.doByteSwap[mode] = false;
- if ( deviceFormat != SND_PCM_FORMAT_S8 ) {
- result = snd_pcm_format_cpu_endian( deviceFormat );
- if ( result == 0 )
- stream_.doByteSwap[mode] = true;
- else if (result < 0) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting pcm device (" << name << ") endian-ness, " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- }
-
- // Set the sample rate.
- result = snd_pcm_hw_params_set_rate_near( phandle, hw_params, (unsigned int*) &sampleRate, 0 );
- if ( result < 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting sample rate on device (" << name << "), " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Determine the number of channels for this device. We support a possible
- // minimum device channel number > than the value requested by the user.
- stream_.nUserChannels[mode] = channels;
- unsigned int value;
- result = snd_pcm_hw_params_get_channels_max( hw_params, &value );
- unsigned int deviceChannels = value;
- if ( result < 0 || deviceChannels < channels + firstChannel ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::probeDeviceOpen: requested channel parameters not supported by device (" << name << "), " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- result = snd_pcm_hw_params_get_channels_min( hw_params, &value );
- if ( result < 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::probeDeviceOpen: error getting minimum channels for device (" << name << "), " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- deviceChannels = value;
- if ( deviceChannels < channels + firstChannel ) deviceChannels = channels + firstChannel;
- stream_.nDeviceChannels[mode] = deviceChannels;
-
- // Set the device channels.
- result = snd_pcm_hw_params_set_channels( phandle, hw_params, deviceChannels );
- if ( result < 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting channels for device (" << name << "), " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Set the buffer (or period) size.
- int dir = 0;
- snd_pcm_uframes_t periodSize = *bufferSize;
- result = snd_pcm_hw_params_set_period_size_near( phandle, hw_params, &periodSize, &dir );
- if ( result < 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting period size for device (" << name << "), " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- *bufferSize = periodSize;
-
- // Set the buffer number, which in ALSA is referred to as the "period".
- unsigned int periods = 0;
- if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) periods = 2;
- if ( options && options->numberOfBuffers > 0 ) periods = options->numberOfBuffers;
- if ( periods < 2 ) periods = 4; // a fairly safe default value
- result = snd_pcm_hw_params_set_periods_near( phandle, hw_params, &periods, &dir );
- if ( result < 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::probeDeviceOpen: error setting periods for device (" << name << "), " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // If attempting to setup a duplex stream, the bufferSize parameter
- // MUST be the same in both directions!
- if ( stream_.mode == OUTPUT && mode == INPUT && *bufferSize != stream_.bufferSize ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::probeDeviceOpen: system error setting buffer size for duplex stream on device (" << name << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- stream_.bufferSize = *bufferSize;
-
- // Install the hardware configuration
- result = snd_pcm_hw_params( phandle, hw_params );
- if ( result < 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing hardware configuration on device (" << name << "), " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
-#if defined(__RTAUDIO_DEBUG__)
- fprintf(stderr, "\nRtApiAlsa: dump hardware params after installation:\n\n");
- snd_pcm_hw_params_dump( hw_params, out );
-#endif
-
- // Set the software configuration to fill buffers with zeros and prevent device stopping on xruns.
- snd_pcm_sw_params_t *sw_params = NULL;
- snd_pcm_sw_params_alloca( &sw_params );
- snd_pcm_sw_params_current( phandle, sw_params );
- snd_pcm_sw_params_set_start_threshold( phandle, sw_params, *bufferSize );
- snd_pcm_sw_params_set_stop_threshold( phandle, sw_params, ULONG_MAX );
- snd_pcm_sw_params_set_silence_threshold( phandle, sw_params, 0 );
-
- // The following two settings were suggested by Theo Veenker
- //snd_pcm_sw_params_set_avail_min( phandle, sw_params, *bufferSize );
- //snd_pcm_sw_params_set_xfer_align( phandle, sw_params, 1 );
-
- // here are two options for a fix
- //snd_pcm_sw_params_set_silence_size( phandle, sw_params, ULONG_MAX );
- snd_pcm_uframes_t val;
- snd_pcm_sw_params_get_boundary( sw_params, &val );
- snd_pcm_sw_params_set_silence_size( phandle, sw_params, val );
-
- result = snd_pcm_sw_params( phandle, sw_params );
- if ( result < 0 ) {
- snd_pcm_close( phandle );
- errorStream_ << "RtApiAlsa::probeDeviceOpen: error installing software configuration on device (" << name << "), " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
-#if defined(__RTAUDIO_DEBUG__)
- fprintf(stderr, "\nRtApiAlsa: dump software params after installation:\n\n");
- snd_pcm_sw_params_dump( sw_params, out );
-#endif
-
- // Set flags for buffer conversion
- stream_.doConvertBuffer[mode] = false;
- if ( stream_.userFormat != stream_.deviceFormat[mode] )
- stream_.doConvertBuffer[mode] = true;
- if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
- stream_.doConvertBuffer[mode] = true;
- if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
- stream_.nUserChannels[mode] > 1 )
- stream_.doConvertBuffer[mode] = true;
-
- // Allocate the ApiHandle if necessary and then save.
- AlsaHandle *apiInfo = 0;
- if ( stream_.apiHandle == 0 ) {
- try {
- apiInfo = (AlsaHandle *) new AlsaHandle;
- }
- catch ( std::bad_alloc& ) {
- errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating AlsaHandle memory.";
- goto error;
- }
-
- if ( pthread_cond_init( &apiInfo->runnable_cv, NULL ) ) {
- errorText_ = "RtApiAlsa::probeDeviceOpen: error initializing pthread condition variable.";
- goto error;
- }
-
- stream_.apiHandle = (void *) apiInfo;
- apiInfo->handles[0] = 0;
- apiInfo->handles[1] = 0;
- }
- else {
- apiInfo = (AlsaHandle *) stream_.apiHandle;
- }
- apiInfo->handles[mode] = phandle;
- phandle = 0;
-
- // Allocate necessary internal buffers.
- unsigned long bufferBytes;
- bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
- stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
- if ( stream_.userBuffer[mode] == NULL ) {
- errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating user buffer memory.";
- goto error;
- }
-
- if ( stream_.doConvertBuffer[mode] ) {
-
- bool makeBuffer = true;
- bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
- if ( mode == INPUT ) {
- if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
- unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
- if ( bufferBytes <= bytesOut ) makeBuffer = false;
- }
- }
-
- if ( makeBuffer ) {
- bufferBytes *= *bufferSize;
- if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
- stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
- if ( stream_.deviceBuffer == NULL ) {
- errorText_ = "RtApiAlsa::probeDeviceOpen: error allocating device buffer memory.";
- goto error;
- }
- }
- }
-
- stream_.sampleRate = sampleRate;
- stream_.nBuffers = periods;
- stream_.device[mode] = device;
- stream_.state = STREAM_STOPPED;
-
- // Setup the buffer conversion information structure.
- if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
-
- // Setup thread if necessary.
- if ( stream_.mode == OUTPUT && mode == INPUT ) {
- // We had already set up an output stream.
- stream_.mode = DUPLEX;
- // Link the streams if possible.
- apiInfo->synchronized = false;
- if ( snd_pcm_link( apiInfo->handles[0], apiInfo->handles[1] ) == 0 )
- apiInfo->synchronized = true;
- else {
- errorText_ = "RtApiAlsa::probeDeviceOpen: unable to synchronize input and output devices.";
- error( RtAudioError::WARNING );
- }
- }
- else {
- stream_.mode = mode;
-
- // Setup callback thread.
- stream_.callbackInfo.object = (void *) this;
-
- // Set the thread attributes for joinable and realtime scheduling
- // priority (optional). The higher priority will only take affect
- // if the program is run as root or suid. Note, under Linux
- // processes with CAP_SYS_NICE privilege, a user can change
- // scheduling policy and priority (thus need not be root). See
- // POSIX "capabilities".
- pthread_attr_t attr;
- pthread_attr_init( &attr );
- pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
-#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread)
- if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {
- stream_.callbackInfo.doRealtime = true;
- struct sched_param param;
- int priority = options->priority;
- int min = sched_get_priority_min( SCHED_RR );
- int max = sched_get_priority_max( SCHED_RR );
- if ( priority < min ) priority = min;
- else if ( priority > max ) priority = max;
- param.sched_priority = priority;
-
- // Set the policy BEFORE the priority. Otherwise it fails.
- pthread_attr_setschedpolicy(&attr, SCHED_RR);
- pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
- // This is definitely required. Otherwise it fails.
- pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
- pthread_attr_setschedparam(&attr, ¶m);
- }
- else
- pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
-#else
- pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
-#endif
-
- stream_.callbackInfo.isRunning = true;
- result = pthread_create( &stream_.callbackInfo.thread, &attr, alsaCallbackHandler, &stream_.callbackInfo );
- pthread_attr_destroy( &attr );
- if ( result ) {
- // Failed. Try instead with default attributes.
- result = pthread_create( &stream_.callbackInfo.thread, NULL, alsaCallbackHandler, &stream_.callbackInfo );
- if ( result ) {
- stream_.callbackInfo.isRunning = false;
- errorText_ = "RtApiAlsa::error creating callback thread!";
- goto error;
- }
- }
- }
-
- return SUCCESS;
-
- error:
- if ( apiInfo ) {
- pthread_cond_destroy( &apiInfo->runnable_cv );
- if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );
- if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );
- delete apiInfo;
- stream_.apiHandle = 0;
- }
-
- if ( phandle) snd_pcm_close( phandle );
-
- for ( int i=0; i<2; i++ ) {
- if ( stream_.userBuffer[i] ) {
- free( stream_.userBuffer[i] );
- stream_.userBuffer[i] = 0;
- }
- }
-
- if ( stream_.deviceBuffer ) {
- free( stream_.deviceBuffer );
- stream_.deviceBuffer = 0;
- }
-
- stream_.state = STREAM_CLOSED;
- return FAILURE;
-}
-
-void RtApiAlsa :: closeStream()
-{
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiAlsa::closeStream(): no open stream to close!";
- error( RtAudioError::WARNING );
- return;
- }
-
- AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
- stream_.callbackInfo.isRunning = false;
- MUTEX_LOCK( &stream_.mutex );
- if ( stream_.state == STREAM_STOPPED ) {
- apiInfo->runnable = true;
- pthread_cond_signal( &apiInfo->runnable_cv );
- }
- MUTEX_UNLOCK( &stream_.mutex );
- pthread_join( stream_.callbackInfo.thread, NULL );
-
- if ( stream_.state == STREAM_RUNNING ) {
- stream_.state = STREAM_STOPPED;
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
- snd_pcm_drop( apiInfo->handles[0] );
- if ( stream_.mode == INPUT || stream_.mode == DUPLEX )
- snd_pcm_drop( apiInfo->handles[1] );
- }
-
- if ( apiInfo ) {
- pthread_cond_destroy( &apiInfo->runnable_cv );
- if ( apiInfo->handles[0] ) snd_pcm_close( apiInfo->handles[0] );
- if ( apiInfo->handles[1] ) snd_pcm_close( apiInfo->handles[1] );
- delete apiInfo;
- stream_.apiHandle = 0;
- }
-
- for ( int i=0; i<2; i++ ) {
- if ( stream_.userBuffer[i] ) {
- free( stream_.userBuffer[i] );
- stream_.userBuffer[i] = 0;
- }
- }
-
- if ( stream_.deviceBuffer ) {
- free( stream_.deviceBuffer );
- stream_.deviceBuffer = 0;
- }
-
- stream_.mode = UNINITIALIZED;
- stream_.state = STREAM_CLOSED;
-}
-
-void RtApiAlsa :: startStream()
-{
- // This method calls snd_pcm_prepare if the device isn't already in that state.
-
- verifyStream();
- if ( stream_.state == STREAM_RUNNING ) {
- errorText_ = "RtApiAlsa::startStream(): the stream is already running!";
- error( RtAudioError::WARNING );
- return;
- }
-
- MUTEX_LOCK( &stream_.mutex );
-
- #if defined( HAVE_GETTIMEOFDAY )
- gettimeofday( &stream_.lastTickTimestamp, NULL );
- #endif
-
- int result = 0;
- snd_pcm_state_t state;
- AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
- snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
- state = snd_pcm_state( handle[0] );
- if ( state != SND_PCM_STATE_PREPARED ) {
- result = snd_pcm_prepare( handle[0] );
- if ( result < 0 ) {
- errorStream_ << "RtApiAlsa::startStream: error preparing output pcm device, " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- goto unlock;
- }
- }
- }
-
- if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
- result = snd_pcm_drop(handle[1]); // fix to remove stale data received since device has been open
- state = snd_pcm_state( handle[1] );
- if ( state != SND_PCM_STATE_PREPARED ) {
- result = snd_pcm_prepare( handle[1] );
- if ( result < 0 ) {
- errorStream_ << "RtApiAlsa::startStream: error preparing input pcm device, " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- goto unlock;
- }
- }
- }
-
- stream_.state = STREAM_RUNNING;
-
- unlock:
- apiInfo->runnable = true;
- pthread_cond_signal( &apiInfo->runnable_cv );
- MUTEX_UNLOCK( &stream_.mutex );
-
- if ( result >= 0 ) return;
- error( RtAudioError::SYSTEM_ERROR );
-}
-
-void RtApiAlsa :: stopStream()
-{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiAlsa::stopStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
- }
-
- stream_.state = STREAM_STOPPED;
- MUTEX_LOCK( &stream_.mutex );
-
- int result = 0;
- AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
- snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
- if ( apiInfo->synchronized )
- result = snd_pcm_drop( handle[0] );
- else
- result = snd_pcm_drain( handle[0] );
- if ( result < 0 ) {
- errorStream_ << "RtApiAlsa::stopStream: error draining output pcm device, " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- goto unlock;
- }
- }
-
- if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
- result = snd_pcm_drop( handle[1] );
- if ( result < 0 ) {
- errorStream_ << "RtApiAlsa::stopStream: error stopping input pcm device, " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- goto unlock;
- }
- }
-
- unlock:
- apiInfo->runnable = false; // fixes high CPU usage when stopped
- MUTEX_UNLOCK( &stream_.mutex );
-
- if ( result >= 0 ) return;
- error( RtAudioError::SYSTEM_ERROR );
-}
-
-void RtApiAlsa :: abortStream()
-{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiAlsa::abortStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
- }
-
- stream_.state = STREAM_STOPPED;
- MUTEX_LOCK( &stream_.mutex );
-
- int result = 0;
- AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
- snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
- result = snd_pcm_drop( handle[0] );
- if ( result < 0 ) {
- errorStream_ << "RtApiAlsa::abortStream: error aborting output pcm device, " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- goto unlock;
- }
- }
-
- if ( ( stream_.mode == INPUT || stream_.mode == DUPLEX ) && !apiInfo->synchronized ) {
- result = snd_pcm_drop( handle[1] );
- if ( result < 0 ) {
- errorStream_ << "RtApiAlsa::abortStream: error aborting input pcm device, " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- goto unlock;
- }
- }
-
- unlock:
- apiInfo->runnable = false; // fixes high CPU usage when stopped
- MUTEX_UNLOCK( &stream_.mutex );
-
- if ( result >= 0 ) return;
- error( RtAudioError::SYSTEM_ERROR );
-}
-
-void RtApiAlsa :: callbackEvent()
-{
- AlsaHandle *apiInfo = (AlsaHandle *) stream_.apiHandle;
- if ( stream_.state == STREAM_STOPPED ) {
- MUTEX_LOCK( &stream_.mutex );
- while ( !apiInfo->runnable )
- pthread_cond_wait( &apiInfo->runnable_cv, &stream_.mutex );
-
- if ( stream_.state != STREAM_RUNNING ) {
- MUTEX_UNLOCK( &stream_.mutex );
- return;
- }
- MUTEX_UNLOCK( &stream_.mutex );
- }
-
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiAlsa::callbackEvent(): the stream is closed ... this shouldn't happen!";
- error( RtAudioError::WARNING );
- return;
- }
-
- int doStopStream = 0;
- RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
- double streamTime = getStreamTime();
- RtAudioStreamStatus status = 0;
- if ( stream_.mode != INPUT && apiInfo->xrun[0] == true ) {
- status |= RTAUDIO_OUTPUT_UNDERFLOW;
- apiInfo->xrun[0] = false;
- }
- if ( stream_.mode != OUTPUT && apiInfo->xrun[1] == true ) {
- status |= RTAUDIO_INPUT_OVERFLOW;
- apiInfo->xrun[1] = false;
- }
- doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
- stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
-
- if ( doStopStream == 2 ) {
- abortStream();
- return;
- }
-
- MUTEX_LOCK( &stream_.mutex );
-
- // The state might change while waiting on a mutex.
- if ( stream_.state == STREAM_STOPPED ) goto unlock;
-
- int result;
- char *buffer;
- int channels;
- snd_pcm_t **handle;
- snd_pcm_sframes_t frames;
- RtAudioFormat format;
- handle = (snd_pcm_t **) apiInfo->handles;
-
- if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
-
- // Setup parameters.
- if ( stream_.doConvertBuffer[1] ) {
- buffer = stream_.deviceBuffer;
- channels = stream_.nDeviceChannels[1];
- format = stream_.deviceFormat[1];
- }
- else {
- buffer = stream_.userBuffer[1];
- channels = stream_.nUserChannels[1];
- format = stream_.userFormat;
- }
-
- // Read samples from device in interleaved/non-interleaved format.
- if ( stream_.deviceInterleaved[1] )
- result = snd_pcm_readi( handle[1], buffer, stream_.bufferSize );
- else {
- void *bufs[channels];
- size_t offset = stream_.bufferSize * formatBytes( format );
- for ( int i=0; i<channels; i++ )
- bufs[i] = (void *) (buffer + (i * offset));
- result = snd_pcm_readn( handle[1], bufs, stream_.bufferSize );
- }
-
- if ( result < (int) stream_.bufferSize ) {
- // Either an error or overrun occured.
- if ( result == -EPIPE ) {
- snd_pcm_state_t state = snd_pcm_state( handle[1] );
- if ( state == SND_PCM_STATE_XRUN ) {
- apiInfo->xrun[1] = true;
- result = snd_pcm_prepare( handle[1] );
- if ( result < 0 ) {
- errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after overrun, " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- }
- }
- else {
- errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- }
- }
- else {
- errorStream_ << "RtApiAlsa::callbackEvent: audio read error, " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- }
- error( RtAudioError::WARNING );
- goto tryOutput;
- }
-
- // Do byte swapping if necessary.
- if ( stream_.doByteSwap[1] )
- byteSwapBuffer( buffer, stream_.bufferSize * channels, format );
-
- // Do buffer conversion if necessary.
- if ( stream_.doConvertBuffer[1] )
- convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
-
- // Check stream latency
- result = snd_pcm_delay( handle[1], &frames );
- if ( result == 0 && frames > 0 ) stream_.latency[1] = frames;
- }
-
- tryOutput:
-
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
-
- // Setup parameters and do buffer conversion if necessary.
- if ( stream_.doConvertBuffer[0] ) {
- buffer = stream_.deviceBuffer;
- convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
- channels = stream_.nDeviceChannels[0];
- format = stream_.deviceFormat[0];
- }
- else {
- buffer = stream_.userBuffer[0];
- channels = stream_.nUserChannels[0];
- format = stream_.userFormat;
- }
-
- // Do byte swapping if necessary.
- if ( stream_.doByteSwap[0] )
- byteSwapBuffer(buffer, stream_.bufferSize * channels, format);
-
- // Write samples to device in interleaved/non-interleaved format.
- if ( stream_.deviceInterleaved[0] )
- result = snd_pcm_writei( handle[0], buffer, stream_.bufferSize );
- else {
- void *bufs[channels];
- size_t offset = stream_.bufferSize * formatBytes( format );
- for ( int i=0; i<channels; i++ )
- bufs[i] = (void *) (buffer + (i * offset));
- result = snd_pcm_writen( handle[0], bufs, stream_.bufferSize );
- }
-
- if ( result < (int) stream_.bufferSize ) {
- // Either an error or underrun occured.
- if ( result == -EPIPE ) {
- snd_pcm_state_t state = snd_pcm_state( handle[0] );
- if ( state == SND_PCM_STATE_XRUN ) {
- apiInfo->xrun[0] = true;
- result = snd_pcm_prepare( handle[0] );
- if ( result < 0 ) {
- errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- }
- else
- errorText_ = "RtApiAlsa::callbackEvent: audio write error, underrun.";
- }
- else {
- errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- }
- }
- else {
- errorStream_ << "RtApiAlsa::callbackEvent: audio write error, " << snd_strerror( result ) << ".";
- errorText_ = errorStream_.str();
- }
- error( RtAudioError::WARNING );
- goto unlock;
- }
-
- // Check stream latency
- result = snd_pcm_delay( handle[0], &frames );
- if ( result == 0 && frames > 0 ) stream_.latency[0] = frames;
- }
-
- unlock:
- MUTEX_UNLOCK( &stream_.mutex );
-
- RtApi::tickStreamTime();
- if ( doStopStream == 1 ) this->stopStream();
-}
-
-static void *alsaCallbackHandler( void *ptr )
-{
- CallbackInfo *info = (CallbackInfo *) ptr;
- RtApiAlsa *object = (RtApiAlsa *) info->object;
- bool *isRunning = &info->isRunning;
-
-#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread)
- if ( info->doRealtime ) {
- std::cerr << "RtAudio alsa: " <<
- (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") <<
- "running realtime scheduling" << std::endl;
- }
-#endif
-
- while ( *isRunning == true ) {
- pthread_testcancel();
- object->callbackEvent();
- }
-
- pthread_exit( NULL );
-}
-
-//******************** End of __LINUX_ALSA__ *********************//
-#endif
-
-#if defined(__LINUX_PULSE__)
-
-// Code written by Peter Meerwald, pmeerw@pmeerw.net
-// and Tristan Matthews.
-
-#include <pulse/error.h>
-#include <pulse/simple.h>
-#include <cstdio>
-
-static const unsigned int SUPPORTED_SAMPLERATES[] = { 8000, 16000, 22050, 32000,
- 44100, 48000, 96000, 0};
-
-struct rtaudio_pa_format_mapping_t {
- RtAudioFormat rtaudio_format;
- pa_sample_format_t pa_format;
-};
-
-static const rtaudio_pa_format_mapping_t supported_sampleformats[] = {
- {RTAUDIO_SINT16, PA_SAMPLE_S16LE},
- {RTAUDIO_SINT32, PA_SAMPLE_S32LE},
- {RTAUDIO_FLOAT32, PA_SAMPLE_FLOAT32LE},
- {0, PA_SAMPLE_INVALID}};
-
-struct PulseAudioHandle {
- pa_simple *s_play;
- pa_simple *s_rec;
- pthread_t thread;
- pthread_cond_t runnable_cv;
- bool runnable;
- PulseAudioHandle() : s_play(0), s_rec(0), runnable(false) { }
-};
-
-RtApiPulse::~RtApiPulse()
-{
- if ( stream_.state != STREAM_CLOSED )
- closeStream();
-}
-
-unsigned int RtApiPulse::getDeviceCount( void )
-{
- return 1;
-}
-
-RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )
-{
- RtAudio::DeviceInfo info;
- info.probed = true;
- info.name = "PulseAudio";
- info.outputChannels = 2;
- info.inputChannels = 2;
- info.duplexChannels = 2;
- info.isDefaultOutput = true;
- info.isDefaultInput = true;
-
- for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr )
- info.sampleRates.push_back( *sr );
-
- info.preferredSampleRate = 48000;
- info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;
-
- return info;
-}
-
-static void *pulseaudio_callback( void * user )
-{
- CallbackInfo *cbi = static_cast<CallbackInfo *>( user );
- RtApiPulse *context = static_cast<RtApiPulse *>( cbi->object );
- volatile bool *isRunning = &cbi->isRunning;
-
-#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread)
- if (cbi->doRealtime) {
- std::cerr << "RtAudio pulse: " <<
- (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") <<
- "running realtime scheduling" << std::endl;
- }
-#endif
-
- while ( *isRunning ) {
- pthread_testcancel();
- context->callbackEvent();
- }
-
- pthread_exit( NULL );
-}
-
-void RtApiPulse::closeStream( void )
-{
- PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
-
- stream_.callbackInfo.isRunning = false;
- if ( pah ) {
- MUTEX_LOCK( &stream_.mutex );
- if ( stream_.state == STREAM_STOPPED ) {
- pah->runnable = true;
- pthread_cond_signal( &pah->runnable_cv );
- }
- MUTEX_UNLOCK( &stream_.mutex );
-
- pthread_join( pah->thread, 0 );
- if ( pah->s_play ) {
- pa_simple_flush( pah->s_play, NULL );
- pa_simple_free( pah->s_play );
- }
- if ( pah->s_rec )
- pa_simple_free( pah->s_rec );
-
- pthread_cond_destroy( &pah->runnable_cv );
- delete pah;
- stream_.apiHandle = 0;
- }
-
- if ( stream_.userBuffer[0] ) {
- free( stream_.userBuffer[0] );
- stream_.userBuffer[0] = 0;
- }
- if ( stream_.userBuffer[1] ) {
- free( stream_.userBuffer[1] );
- stream_.userBuffer[1] = 0;
- }
-
- stream_.state = STREAM_CLOSED;
- stream_.mode = UNINITIALIZED;
-}
-
-void RtApiPulse::callbackEvent( void )
-{
- PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
-
- if ( stream_.state == STREAM_STOPPED ) {
- MUTEX_LOCK( &stream_.mutex );
- while ( !pah->runnable )
- pthread_cond_wait( &pah->runnable_cv, &stream_.mutex );
-
- if ( stream_.state != STREAM_RUNNING ) {
- MUTEX_UNLOCK( &stream_.mutex );
- return;
- }
- MUTEX_UNLOCK( &stream_.mutex );
- }
-
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiPulse::callbackEvent(): the stream is closed ... "
- "this shouldn't happen!";
- error( RtAudioError::WARNING );
- return;
- }
-
- RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
- double streamTime = getStreamTime();
- RtAudioStreamStatus status = 0;
- int doStopStream = callback( stream_.userBuffer[OUTPUT], stream_.userBuffer[INPUT],
- stream_.bufferSize, streamTime, status,
- stream_.callbackInfo.userData );
-
- if ( doStopStream == 2 ) {
- abortStream();
- return;
- }
-
- MUTEX_LOCK( &stream_.mutex );
- void *pulse_in = stream_.doConvertBuffer[INPUT] ? stream_.deviceBuffer : stream_.userBuffer[INPUT];
- void *pulse_out = stream_.doConvertBuffer[OUTPUT] ? stream_.deviceBuffer : stream_.userBuffer[OUTPUT];
-
- if ( stream_.state != STREAM_RUNNING )
- goto unlock;
-
- int pa_error;
- size_t bytes;
- if (stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
- if ( stream_.doConvertBuffer[OUTPUT] ) {
- convertBuffer( stream_.deviceBuffer,
- stream_.userBuffer[OUTPUT],
- stream_.convertInfo[OUTPUT] );
- bytes = stream_.nDeviceChannels[OUTPUT] * stream_.bufferSize *
- formatBytes( stream_.deviceFormat[OUTPUT] );
- } else
- bytes = stream_.nUserChannels[OUTPUT] * stream_.bufferSize *
- formatBytes( stream_.userFormat );
-
- if ( pa_simple_write( pah->s_play, pulse_out, bytes, &pa_error ) < 0 ) {
- errorStream_ << "RtApiPulse::callbackEvent: audio write error, " <<
- pa_strerror( pa_error ) << ".";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- }
- }
-
- if ( stream_.mode == INPUT || stream_.mode == DUPLEX) {
- if ( stream_.doConvertBuffer[INPUT] )
- bytes = stream_.nDeviceChannels[INPUT] * stream_.bufferSize *
- formatBytes( stream_.deviceFormat[INPUT] );
- else
- bytes = stream_.nUserChannels[INPUT] * stream_.bufferSize *
- formatBytes( stream_.userFormat );
-
- if ( pa_simple_read( pah->s_rec, pulse_in, bytes, &pa_error ) < 0 ) {
- errorStream_ << "RtApiPulse::callbackEvent: audio read error, " <<
- pa_strerror( pa_error ) << ".";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- }
- if ( stream_.doConvertBuffer[INPUT] ) {
- convertBuffer( stream_.userBuffer[INPUT],
- stream_.deviceBuffer,
- stream_.convertInfo[INPUT] );
- }
- }
-
- unlock:
- MUTEX_UNLOCK( &stream_.mutex );
- RtApi::tickStreamTime();
-
- if ( doStopStream == 1 )
- stopStream();
-}
-
-void RtApiPulse::startStream( void )
-{
- PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
-
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiPulse::startStream(): the stream is not open!";
- error( RtAudioError::INVALID_USE );
- return;
- }
- if ( stream_.state == STREAM_RUNNING ) {
- errorText_ = "RtApiPulse::startStream(): the stream is already running!";
- error( RtAudioError::WARNING );
- return;
- }
-
- MUTEX_LOCK( &stream_.mutex );
-
- #if defined( HAVE_GETTIMEOFDAY )
- gettimeofday( &stream_.lastTickTimestamp, NULL );
- #endif
-
- stream_.state = STREAM_RUNNING;
-
- pah->runnable = true;
- pthread_cond_signal( &pah->runnable_cv );
- MUTEX_UNLOCK( &stream_.mutex );
-}
-
-void RtApiPulse::stopStream( void )
-{
- PulseAudioHandle *pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
-
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiPulse::stopStream(): the stream is not open!";
- error( RtAudioError::INVALID_USE );
- return;
- }
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiPulse::stopStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
- }
-
- stream_.state = STREAM_STOPPED;
- MUTEX_LOCK( &stream_.mutex );
-
- if ( pah ) {
- pah->runnable = false;
- if ( pah->s_play ) {
- int pa_error;
- if ( pa_simple_drain( pah->s_play, &pa_error ) < 0 ) {
- errorStream_ << "RtApiPulse::stopStream: error draining output device, " <<
- pa_strerror( pa_error ) << ".";
- errorText_ = errorStream_.str();
- MUTEX_UNLOCK( &stream_.mutex );
- error( RtAudioError::SYSTEM_ERROR );
- return;
- }
- }
- }
-
- stream_.state = STREAM_STOPPED;
- MUTEX_UNLOCK( &stream_.mutex );
-}
-
-void RtApiPulse::abortStream( void )
-{
- PulseAudioHandle *pah = static_cast<PulseAudioHandle*>( stream_.apiHandle );
-
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiPulse::abortStream(): the stream is not open!";
- error( RtAudioError::INVALID_USE );
- return;
- }
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiPulse::abortStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
- }
-
- stream_.state = STREAM_STOPPED;
- MUTEX_LOCK( &stream_.mutex );
-
- if ( pah ) {
- pah->runnable = false;
- if ( pah->s_play ) {
- int pa_error;
- if ( pa_simple_flush( pah->s_play, &pa_error ) < 0 ) {
- errorStream_ << "RtApiPulse::abortStream: error flushing output device, " <<
- pa_strerror( pa_error ) << ".";
- errorText_ = errorStream_.str();
- MUTEX_UNLOCK( &stream_.mutex );
- error( RtAudioError::SYSTEM_ERROR );
- return;
- }
- }
- }
-
- stream_.state = STREAM_STOPPED;
- MUTEX_UNLOCK( &stream_.mutex );
-}
-
-bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
- unsigned int channels, unsigned int firstChannel,
- unsigned int sampleRate, RtAudioFormat format,
- unsigned int *bufferSize, RtAudio::StreamOptions *options )
-{
- PulseAudioHandle *pah = 0;
- unsigned long bufferBytes = 0;
- pa_sample_spec ss;
-
- if ( device != 0 ) return false;
- if ( mode != INPUT && mode != OUTPUT ) return false;
- if ( channels != 1 && channels != 2 ) {
- errorText_ = "RtApiPulse::probeDeviceOpen: unsupported number of channels.";
- return false;
- }
- ss.channels = channels;
-
- if ( firstChannel != 0 ) return false;
-
- bool sr_found = false;
- for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) {
- if ( sampleRate == *sr ) {
- sr_found = true;
- stream_.sampleRate = sampleRate;
- ss.rate = sampleRate;
- break;
- }
- }
- if ( !sr_found ) {
- errorText_ = "RtApiPulse::probeDeviceOpen: unsupported sample rate.";
- return false;
- }
-
- bool sf_found = 0;
- for ( const rtaudio_pa_format_mapping_t *sf = supported_sampleformats;
- sf->rtaudio_format && sf->pa_format != PA_SAMPLE_INVALID; ++sf ) {
- if ( format == sf->rtaudio_format ) {
- sf_found = true;
- stream_.userFormat = sf->rtaudio_format;
- stream_.deviceFormat[mode] = stream_.userFormat;
- ss.format = sf->pa_format;
- break;
- }
- }
- if ( !sf_found ) { // Use internal data format conversion.
- stream_.userFormat = format;
- stream_.deviceFormat[mode] = RTAUDIO_FLOAT32;
- ss.format = PA_SAMPLE_FLOAT32LE;
- }
-
- // Set other stream parameters.
- if ( options && options->flags & RTAUDIO_NONINTERLEAVED ) stream_.userInterleaved = false;
- else stream_.userInterleaved = true;
- stream_.deviceInterleaved[mode] = true;
- stream_.nBuffers = 1;
- stream_.doByteSwap[mode] = false;
- stream_.nUserChannels[mode] = channels;
- stream_.nDeviceChannels[mode] = channels + firstChannel;
- stream_.channelOffset[mode] = 0;
- std::string streamName = "RtAudio";
-
- // Set flags for buffer conversion.
- stream_.doConvertBuffer[mode] = false;
- if ( stream_.userFormat != stream_.deviceFormat[mode] )
- stream_.doConvertBuffer[mode] = true;
- if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
- stream_.doConvertBuffer[mode] = true;
- if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] )
- stream_.doConvertBuffer[mode] = true;
-
- // Allocate necessary internal buffers.
- bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
- stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
- if ( stream_.userBuffer[mode] == NULL ) {
- errorText_ = "RtApiPulse::probeDeviceOpen: error allocating user buffer memory.";
- goto error;
- }
- stream_.bufferSize = *bufferSize;
-
- if ( stream_.doConvertBuffer[mode] ) {
-
- bool makeBuffer = true;
- bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
- if ( mode == INPUT ) {
- if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
- unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
- if ( bufferBytes <= bytesOut ) makeBuffer = false;
- }
- }
-
- if ( makeBuffer ) {
- bufferBytes *= *bufferSize;
- if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
- stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
- if ( stream_.deviceBuffer == NULL ) {
- errorText_ = "RtApiPulse::probeDeviceOpen: error allocating device buffer memory.";
- goto error;
- }
- }
- }
-
- stream_.device[mode] = device;
-
- // Setup the buffer conversion information structure.
- if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
-
- if ( !stream_.apiHandle ) {
- PulseAudioHandle *pah = new PulseAudioHandle;
- if ( !pah ) {
- errorText_ = "RtApiPulse::probeDeviceOpen: error allocating memory for handle.";
- goto error;
- }
-
- stream_.apiHandle = pah;
- if ( pthread_cond_init( &pah->runnable_cv, NULL ) != 0 ) {
- errorText_ = "RtApiPulse::probeDeviceOpen: error creating condition variable.";
- goto error;
- }
- }
- pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
-
- int error;
- if ( options && !options->streamName.empty() ) streamName = options->streamName;
- switch ( mode ) {
- case INPUT:
- pa_buffer_attr buffer_attr;
- buffer_attr.fragsize = bufferBytes;
- buffer_attr.maxlength = -1;
-
- pah->s_rec = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_RECORD, NULL, "Record", &ss, NULL, &buffer_attr, &error );
- if ( !pah->s_rec ) {
- errorText_ = "RtApiPulse::probeDeviceOpen: error connecting input to PulseAudio server.";
- goto error;
- }
- break;
- case OUTPUT:
- pah->s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );
- if ( !pah->s_play ) {
- errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";
- goto error;
- }
- break;
- default:
- goto error;
- }
-
- if ( stream_.mode == UNINITIALIZED )
- stream_.mode = mode;
- else if ( stream_.mode == mode )
- goto error;
- else
- stream_.mode = DUPLEX;
-
- if ( !stream_.callbackInfo.isRunning ) {
- stream_.callbackInfo.object = this;
-
- stream_.state = STREAM_STOPPED;
- // Set the thread attributes for joinable and realtime scheduling
- // priority (optional). The higher priority will only take affect
- // if the program is run as root or suid. Note, under Linux
- // processes with CAP_SYS_NICE privilege, a user can change
- // scheduling policy and priority (thus need not be root). See
- // POSIX "capabilities".
- pthread_attr_t attr;
- pthread_attr_init( &attr );
- pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
-#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread)
- if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {
- stream_.callbackInfo.doRealtime = true;
- struct sched_param param;
- int priority = options->priority;
- int min = sched_get_priority_min( SCHED_RR );
- int max = sched_get_priority_max( SCHED_RR );
- if ( priority < min ) priority = min;
- else if ( priority > max ) priority = max;
- param.sched_priority = priority;
-
- // Set the policy BEFORE the priority. Otherwise it fails.
- pthread_attr_setschedpolicy(&attr, SCHED_RR);
- pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
- // This is definitely required. Otherwise it fails.
- pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
- pthread_attr_setschedparam(&attr, ¶m);
- }
- else
- pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
-#else
- pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
-#endif
-
- stream_.callbackInfo.isRunning = true;
- int result = pthread_create( &pah->thread, &attr, pulseaudio_callback, (void *)&stream_.callbackInfo);
- pthread_attr_destroy(&attr);
- if(result != 0) {
- // Failed. Try instead with default attributes.
- result = pthread_create( &pah->thread, NULL, pulseaudio_callback, (void *)&stream_.callbackInfo);
- if(result != 0) {
- stream_.callbackInfo.isRunning = false;
- errorText_ = "RtApiPulse::probeDeviceOpen: error creating thread.";
- goto error;
- }
- }
- }
-
- return SUCCESS;
-
- error:
- if ( pah && stream_.callbackInfo.isRunning ) {
- pthread_cond_destroy( &pah->runnable_cv );
- delete pah;
- stream_.apiHandle = 0;
- }
-
- for ( int i=0; i<2; i++ ) {
- if ( stream_.userBuffer[i] ) {
- free( stream_.userBuffer[i] );
- stream_.userBuffer[i] = 0;
- }
- }
-
- if ( stream_.deviceBuffer ) {
- free( stream_.deviceBuffer );
- stream_.deviceBuffer = 0;
- }
-
- stream_.state = STREAM_CLOSED;
- return FAILURE;
-}
-
-//******************** End of __LINUX_PULSE__ *********************//
-#endif
-
-#if defined(__LINUX_OSS__)
-
-#include <unistd.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/soundcard.h>
-#include <errno.h>
-#include <math.h>
-
-static void *ossCallbackHandler(void * ptr);
-
-// A structure to hold various information related to the OSS API
-// implementation.
-struct OssHandle {
- int id[2]; // device ids
- bool xrun[2];
- bool triggered;
- pthread_cond_t runnable;
-
- OssHandle()
- :triggered(false) { id[0] = 0; id[1] = 0; xrun[0] = false; xrun[1] = false; }
-};
-
-RtApiOss :: RtApiOss()
-{
- // Nothing to do here.
-}
-
-RtApiOss :: ~RtApiOss()
-{
- if ( stream_.state != STREAM_CLOSED ) closeStream();
-}
-
-unsigned int RtApiOss :: getDeviceCount( void )
-{
- int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
- if ( mixerfd == -1 ) {
- errorText_ = "RtApiOss::getDeviceCount: error opening '/dev/mixer'.";
- error( RtAudioError::WARNING );
- return 0;
- }
-
- oss_sysinfo sysinfo;
- if ( ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo ) == -1 ) {
- close( mixerfd );
- errorText_ = "RtApiOss::getDeviceCount: error getting sysinfo, OSS version >= 4.0 is required.";
- error( RtAudioError::WARNING );
- return 0;
- }
-
- close( mixerfd );
- return sysinfo.numaudios;
-}
-
-RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
-{
- RtAudio::DeviceInfo info;
- info.probed = false;
-
- int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
- if ( mixerfd == -1 ) {
- errorText_ = "RtApiOss::getDeviceInfo: error opening '/dev/mixer'.";
- error( RtAudioError::WARNING );
- return info;
- }
-
- oss_sysinfo sysinfo;
- int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );
- if ( result == -1 ) {
- close( mixerfd );
- errorText_ = "RtApiOss::getDeviceInfo: error getting sysinfo, OSS version >= 4.0 is required.";
- error( RtAudioError::WARNING );
- return info;
- }
-
- unsigned nDevices = sysinfo.numaudios;
- if ( nDevices == 0 ) {
- close( mixerfd );
- errorText_ = "RtApiOss::getDeviceInfo: no devices found!";
- error( RtAudioError::INVALID_USE );
- return info;
- }
-
- if ( device >= nDevices ) {
- close( mixerfd );
- errorText_ = "RtApiOss::getDeviceInfo: device ID is invalid!";
- error( RtAudioError::INVALID_USE );
- return info;
- }
-
- oss_audioinfo ainfo;
- ainfo.dev = device;
- result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );
- close( mixerfd );
- if ( result == -1 ) {
- errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- // Probe channels
- if ( ainfo.caps & PCM_CAP_OUTPUT ) info.outputChannels = ainfo.max_channels;
- if ( ainfo.caps & PCM_CAP_INPUT ) info.inputChannels = ainfo.max_channels;
- if ( ainfo.caps & PCM_CAP_DUPLEX ) {
- if ( info.outputChannels > 0 && info.inputChannels > 0 && ainfo.caps & PCM_CAP_DUPLEX )
- info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
- }
-
- // Probe data formats ... do for input
- unsigned long mask = ainfo.iformats;
- if ( mask & AFMT_S16_LE || mask & AFMT_S16_BE )
- info.nativeFormats |= RTAUDIO_SINT16;
- if ( mask & AFMT_S8 )
- info.nativeFormats |= RTAUDIO_SINT8;
- if ( mask & AFMT_S32_LE || mask & AFMT_S32_BE )
- info.nativeFormats |= RTAUDIO_SINT32;
-#ifdef AFMT_FLOAT
- if ( mask & AFMT_FLOAT )
- info.nativeFormats |= RTAUDIO_FLOAT32;
-#endif
- if ( mask & AFMT_S24_LE || mask & AFMT_S24_BE )
- info.nativeFormats |= RTAUDIO_SINT24;
-
- // Check that we have at least one supported format
- if ( info.nativeFormats == 0 ) {
- errorStream_ << "RtApiOss::getDeviceInfo: device (" << ainfo.name << ") data format not supported by RtAudio.";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- return info;
- }
-
- // Probe the supported sample rates.
- info.sampleRates.clear();
- if ( ainfo.nrates ) {
- for ( unsigned int i=0; i<ainfo.nrates; i++ ) {
- for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
- if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {
- info.sampleRates.push_back( SAMPLE_RATES[k] );
-
- if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
- info.preferredSampleRate = SAMPLE_RATES[k];
-
- break;
- }
- }
- }
- }
- else {
- // Check min and max rate values;
- for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
- if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] ) {
- info.sampleRates.push_back( SAMPLE_RATES[k] );
-
- if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
- info.preferredSampleRate = SAMPLE_RATES[k];
- }
- }
- }
-
- if ( info.sampleRates.size() == 0 ) {
- errorStream_ << "RtApiOss::getDeviceInfo: no supported sample rates found for device (" << ainfo.name << ").";
- errorText_ = errorStream_.str();
- error( RtAudioError::WARNING );
- }
- else {
- info.probed = true;
- info.name = ainfo.name;
- }
-
- return info;
-}
-
-
-bool RtApiOss :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
- RtAudioFormat format, unsigned int *bufferSize,
- RtAudio::StreamOptions *options )
-{
- int mixerfd = open( "/dev/mixer", O_RDWR, 0 );
- if ( mixerfd == -1 ) {
- errorText_ = "RtApiOss::probeDeviceOpen: error opening '/dev/mixer'.";
- return FAILURE;
- }
-
- oss_sysinfo sysinfo;
- int result = ioctl( mixerfd, SNDCTL_SYSINFO, &sysinfo );
- if ( result == -1 ) {
- close( mixerfd );
- errorText_ = "RtApiOss::probeDeviceOpen: error getting sysinfo, OSS version >= 4.0 is required.";
- return FAILURE;
- }
-
- unsigned nDevices = sysinfo.numaudios;
- if ( nDevices == 0 ) {
- // This should not happen because a check is made before this function is called.
- close( mixerfd );
- errorText_ = "RtApiOss::probeDeviceOpen: no devices found!";
- return FAILURE;
- }
-
- if ( device >= nDevices ) {
- // This should not happen because a check is made before this function is called.
- close( mixerfd );
- errorText_ = "RtApiOss::probeDeviceOpen: device ID is invalid!";
- return FAILURE;
- }
-
- oss_audioinfo ainfo;
- ainfo.dev = device;
- result = ioctl( mixerfd, SNDCTL_AUDIOINFO, &ainfo );
- close( mixerfd );
- if ( result == -1 ) {
- errorStream_ << "RtApiOss::getDeviceInfo: error getting device (" << ainfo.name << ") info.";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Check if device supports input or output
- if ( ( mode == OUTPUT && !( ainfo.caps & PCM_CAP_OUTPUT ) ) ||
- ( mode == INPUT && !( ainfo.caps & PCM_CAP_INPUT ) ) ) {
- if ( mode == OUTPUT )
- errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support output.";
- else
- errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support input.";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- int flags = 0;
- OssHandle *handle = (OssHandle *) stream_.apiHandle;
- if ( mode == OUTPUT )
- flags |= O_WRONLY;
- else { // mode == INPUT
- if (stream_.mode == OUTPUT && stream_.device[0] == device) {
- // We just set the same device for playback ... close and reopen for duplex (OSS only).
- close( handle->id[0] );
- handle->id[0] = 0;
- if ( !( ainfo.caps & PCM_CAP_DUPLEX ) ) {
- errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support duplex mode.";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- // Check that the number previously set channels is the same.
- if ( stream_.nUserChannels[0] != channels ) {
- errorStream_ << "RtApiOss::probeDeviceOpen: input/output channels must be equal for OSS duplex device (" << ainfo.name << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- flags |= O_RDWR;
- }
- else
- flags |= O_RDONLY;
- }
-
- // Set exclusive access if specified.
- if ( options && options->flags & RTAUDIO_HOG_DEVICE ) flags |= O_EXCL;
-
- // Try to open the device.
- int fd;
- fd = open( ainfo.devnode, flags, 0 );
- if ( fd == -1 ) {
- if ( errno == EBUSY )
- errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") is busy.";
- else
- errorStream_ << "RtApiOss::probeDeviceOpen: error opening device (" << ainfo.name << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // For duplex operation, specifically set this mode (this doesn't seem to work).
- /*
- if ( flags | O_RDWR ) {
- result = ioctl( fd, SNDCTL_DSP_SETDUPLEX, NULL );
- if ( result == -1) {
- errorStream_ << "RtApiOss::probeDeviceOpen: error setting duplex mode for device (" << ainfo.name << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- }
- */
-
- // Check the device channel support.
- stream_.nUserChannels[mode] = channels;
- if ( ainfo.max_channels < (int)(channels + firstChannel) ) {
- close( fd );
- errorStream_ << "RtApiOss::probeDeviceOpen: the device (" << ainfo.name << ") does not support requested channel parameters.";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Set the number of channels.
- int deviceChannels = channels + firstChannel;
- result = ioctl( fd, SNDCTL_DSP_CHANNELS, &deviceChannels );
- if ( result == -1 || deviceChannels < (int)(channels + firstChannel) ) {
- close( fd );
- errorStream_ << "RtApiOss::probeDeviceOpen: error setting channel parameters on device (" << ainfo.name << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- stream_.nDeviceChannels[mode] = deviceChannels;
-
- // Get the data format mask
- int mask;
- result = ioctl( fd, SNDCTL_DSP_GETFMTS, &mask );
- if ( result == -1 ) {
- close( fd );
- errorStream_ << "RtApiOss::probeDeviceOpen: error getting device (" << ainfo.name << ") data formats.";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Determine how to set the device format.
- stream_.userFormat = format;
- int deviceFormat = -1;
- stream_.doByteSwap[mode] = false;
- if ( format == RTAUDIO_SINT8 ) {
- if ( mask & AFMT_S8 ) {
- deviceFormat = AFMT_S8;
- stream_.deviceFormat[mode] = RTAUDIO_SINT8;
- }
- }
- else if ( format == RTAUDIO_SINT16 ) {
- if ( mask & AFMT_S16_NE ) {
- deviceFormat = AFMT_S16_NE;
- stream_.deviceFormat[mode] = RTAUDIO_SINT16;
- }
- else if ( mask & AFMT_S16_OE ) {
- deviceFormat = AFMT_S16_OE;
- stream_.deviceFormat[mode] = RTAUDIO_SINT16;
- stream_.doByteSwap[mode] = true;
- }
- }
- else if ( format == RTAUDIO_SINT24 ) {
- if ( mask & AFMT_S24_NE ) {
- deviceFormat = AFMT_S24_NE;
- stream_.deviceFormat[mode] = RTAUDIO_SINT24;
- }
- else if ( mask & AFMT_S24_OE ) {
- deviceFormat = AFMT_S24_OE;
- stream_.deviceFormat[mode] = RTAUDIO_SINT24;
- stream_.doByteSwap[mode] = true;
- }
- }
- else if ( format == RTAUDIO_SINT32 ) {
- if ( mask & AFMT_S32_NE ) {
- deviceFormat = AFMT_S32_NE;
- stream_.deviceFormat[mode] = RTAUDIO_SINT32;
- }
- else if ( mask & AFMT_S32_OE ) {
- deviceFormat = AFMT_S32_OE;
- stream_.deviceFormat[mode] = RTAUDIO_SINT32;
- stream_.doByteSwap[mode] = true;
- }
- }
-
- if ( deviceFormat == -1 ) {
- // The user requested format is not natively supported by the device.
- if ( mask & AFMT_S16_NE ) {
- deviceFormat = AFMT_S16_NE;
- stream_.deviceFormat[mode] = RTAUDIO_SINT16;
- }
- else if ( mask & AFMT_S32_NE ) {
- deviceFormat = AFMT_S32_NE;
- stream_.deviceFormat[mode] = RTAUDIO_SINT32;
- }
- else if ( mask & AFMT_S24_NE ) {
- deviceFormat = AFMT_S24_NE;
- stream_.deviceFormat[mode] = RTAUDIO_SINT24;
- }
- else if ( mask & AFMT_S16_OE ) {
- deviceFormat = AFMT_S16_OE;
- stream_.deviceFormat[mode] = RTAUDIO_SINT16;
- stream_.doByteSwap[mode] = true;
- }
- else if ( mask & AFMT_S32_OE ) {
- deviceFormat = AFMT_S32_OE;
- stream_.deviceFormat[mode] = RTAUDIO_SINT32;
- stream_.doByteSwap[mode] = true;
- }
- else if ( mask & AFMT_S24_OE ) {
- deviceFormat = AFMT_S24_OE;
- stream_.deviceFormat[mode] = RTAUDIO_SINT24;
- stream_.doByteSwap[mode] = true;
- }
- else if ( mask & AFMT_S8) {
- deviceFormat = AFMT_S8;
- stream_.deviceFormat[mode] = RTAUDIO_SINT8;
- }
- }
-
- if ( stream_.deviceFormat[mode] == 0 ) {
- // This really shouldn't happen ...
- close( fd );
- errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") data format not supported by RtAudio.";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Set the data format.
- int temp = deviceFormat;
- result = ioctl( fd, SNDCTL_DSP_SETFMT, &deviceFormat );
- if ( result == -1 || deviceFormat != temp ) {
- close( fd );
- errorStream_ << "RtApiOss::probeDeviceOpen: error setting data format on device (" << ainfo.name << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Attempt to set the buffer size. According to OSS, the minimum
- // number of buffers is two. The supposed minimum buffer size is 16
- // bytes, so that will be our lower bound. The argument to this
- // call is in the form 0xMMMMSSSS (hex), where the buffer size (in
- // bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.
- // We'll check the actual value used near the end of the setup
- // procedure.
- int ossBufferBytes = *bufferSize * formatBytes( stream_.deviceFormat[mode] ) * deviceChannels;
- if ( ossBufferBytes < 16 ) ossBufferBytes = 16;
- int buffers = 0;
- if ( options ) buffers = options->numberOfBuffers;
- if ( options && options->flags & RTAUDIO_MINIMIZE_LATENCY ) buffers = 2;
- if ( buffers < 2 ) buffers = 3;
- temp = ((int) buffers << 16) + (int)( log10( (double)ossBufferBytes ) / log10( 2.0 ) );
- result = ioctl( fd, SNDCTL_DSP_SETFRAGMENT, &temp );
- if ( result == -1 ) {
- close( fd );
- errorStream_ << "RtApiOss::probeDeviceOpen: error setting buffer size on device (" << ainfo.name << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- stream_.nBuffers = buffers;
-
- // Save buffer size (in sample frames).
- *bufferSize = ossBufferBytes / ( formatBytes(stream_.deviceFormat[mode]) * deviceChannels );
- stream_.bufferSize = *bufferSize;
-
- // Set the sample rate.
- int srate = sampleRate;
- result = ioctl( fd, SNDCTL_DSP_SPEED, &srate );
- if ( result == -1 ) {
- close( fd );
- errorStream_ << "RtApiOss::probeDeviceOpen: error setting sample rate (" << sampleRate << ") on device (" << ainfo.name << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
-
- // Verify the sample rate setup worked.
- if ( abs( srate - (int)sampleRate ) > 100 ) {
- close( fd );
- errorStream_ << "RtApiOss::probeDeviceOpen: device (" << ainfo.name << ") does not support sample rate (" << sampleRate << ").";
- errorText_ = errorStream_.str();
- return FAILURE;
- }
- stream_.sampleRate = sampleRate;
-
- if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] == device) {
- // We're doing duplex setup here.
- stream_.deviceFormat[0] = stream_.deviceFormat[1];
- stream_.nDeviceChannels[0] = deviceChannels;
- }
-
- // Set interleaving parameters.
- stream_.userInterleaved = true;
- stream_.deviceInterleaved[mode] = true;
- if ( options && options->flags & RTAUDIO_NONINTERLEAVED )
- stream_.userInterleaved = false;
-
- // Set flags for buffer conversion
- stream_.doConvertBuffer[mode] = false;
- if ( stream_.userFormat != stream_.deviceFormat[mode] )
- stream_.doConvertBuffer[mode] = true;
- if ( stream_.nUserChannels[mode] < stream_.nDeviceChannels[mode] )
- stream_.doConvertBuffer[mode] = true;
- if ( stream_.userInterleaved != stream_.deviceInterleaved[mode] &&
- stream_.nUserChannels[mode] > 1 )
- stream_.doConvertBuffer[mode] = true;
-
- // Allocate the stream handles if necessary and then save.
- if ( stream_.apiHandle == 0 ) {
- try {
- handle = new OssHandle;
- }
- catch ( std::bad_alloc& ) {
- errorText_ = "RtApiOss::probeDeviceOpen: error allocating OssHandle memory.";
- goto error;
- }
-
- if ( pthread_cond_init( &handle->runnable, NULL ) ) {
- errorText_ = "RtApiOss::probeDeviceOpen: error initializing pthread condition variable.";
- goto error;
- }
-
- stream_.apiHandle = (void *) handle;
- }
- else {
- handle = (OssHandle *) stream_.apiHandle;
- }
- handle->id[mode] = fd;
-
- // Allocate necessary internal buffers.
- unsigned long bufferBytes;
- bufferBytes = stream_.nUserChannels[mode] * *bufferSize * formatBytes( stream_.userFormat );
- stream_.userBuffer[mode] = (char *) calloc( bufferBytes, 1 );
- if ( stream_.userBuffer[mode] == NULL ) {
- errorText_ = "RtApiOss::probeDeviceOpen: error allocating user buffer memory.";
- goto error;
- }
-
- if ( stream_.doConvertBuffer[mode] ) {
-
- bool makeBuffer = true;
- bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
- if ( mode == INPUT ) {
- if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) {
- unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
- if ( bufferBytes <= bytesOut ) makeBuffer = false;
- }
- }
-
- if ( makeBuffer ) {
- bufferBytes *= *bufferSize;
- if ( stream_.deviceBuffer ) free( stream_.deviceBuffer );
- stream_.deviceBuffer = (char *) calloc( bufferBytes, 1 );
- if ( stream_.deviceBuffer == NULL ) {
- errorText_ = "RtApiOss::probeDeviceOpen: error allocating device buffer memory.";
- goto error;
- }
- }
- }
-
- stream_.device[mode] = device;
- stream_.state = STREAM_STOPPED;
-
- // Setup the buffer conversion information structure.
- if ( stream_.doConvertBuffer[mode] ) setConvertInfo( mode, firstChannel );
-
- // Setup thread if necessary.
- if ( stream_.mode == OUTPUT && mode == INPUT ) {
- // We had already set up an output stream.
- stream_.mode = DUPLEX;
- if ( stream_.device[0] == device ) handle->id[0] = fd;
- }
- else {
- stream_.mode = mode;
-
- // Setup callback thread.
- stream_.callbackInfo.object = (void *) this;
-
- // Set the thread attributes for joinable and realtime scheduling
- // priority. The higher priority will only take affect if the
- // program is run as root or suid.
- pthread_attr_t attr;
- pthread_attr_init( &attr );
- pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE );
-#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread)
- if ( options && options->flags & RTAUDIO_SCHEDULE_REALTIME ) {
- stream_.callbackInfo.doRealtime = true;
- struct sched_param param;
- int priority = options->priority;
- int min = sched_get_priority_min( SCHED_RR );
- int max = sched_get_priority_max( SCHED_RR );
- if ( priority < min ) priority = min;
- else if ( priority > max ) priority = max;
- param.sched_priority = priority;
-
- // Set the policy BEFORE the priority. Otherwise it fails.
- pthread_attr_setschedpolicy(&attr, SCHED_RR);
- pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
- // This is definitely required. Otherwise it fails.
- pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
- pthread_attr_setschedparam(&attr, ¶m);
- }
- else
- pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
-#else
- pthread_attr_setschedpolicy( &attr, SCHED_OTHER );
-#endif
-
- stream_.callbackInfo.isRunning = true;
- result = pthread_create( &stream_.callbackInfo.thread, &attr, ossCallbackHandler, &stream_.callbackInfo );
- pthread_attr_destroy( &attr );
- if ( result ) {
- // Failed. Try instead with default attributes.
- result = pthread_create( &stream_.callbackInfo.thread, NULL, ossCallbackHandler, &stream_.callbackInfo );
- if ( result ) {
- stream_.callbackInfo.isRunning = false;
- errorText_ = "RtApiOss::error creating callback thread!";
- goto error;
- }
- }
- }
-
- return SUCCESS;
-
- error:
- if ( handle ) {
- pthread_cond_destroy( &handle->runnable );
- if ( handle->id[0] ) close( handle->id[0] );
- if ( handle->id[1] ) close( handle->id[1] );
- delete handle;
- stream_.apiHandle = 0;
- }
-
- for ( int i=0; i<2; i++ ) {
- if ( stream_.userBuffer[i] ) {
- free( stream_.userBuffer[i] );
- stream_.userBuffer[i] = 0;
- }
- }
-
- if ( stream_.deviceBuffer ) {
- free( stream_.deviceBuffer );
- stream_.deviceBuffer = 0;
- }
-
- stream_.state = STREAM_CLOSED;
- return FAILURE;
-}
-
-void RtApiOss :: closeStream()
-{
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiOss::closeStream(): no open stream to close!";
- error( RtAudioError::WARNING );
- return;
- }
-
- OssHandle *handle = (OssHandle *) stream_.apiHandle;
- stream_.callbackInfo.isRunning = false;
- MUTEX_LOCK( &stream_.mutex );
- if ( stream_.state == STREAM_STOPPED )
- pthread_cond_signal( &handle->runnable );
- MUTEX_UNLOCK( &stream_.mutex );
- pthread_join( stream_.callbackInfo.thread, NULL );
-
- if ( stream_.state == STREAM_RUNNING ) {
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX )
- ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
- else
- ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
- stream_.state = STREAM_STOPPED;
- }
-
- if ( handle ) {
- pthread_cond_destroy( &handle->runnable );
- if ( handle->id[0] ) close( handle->id[0] );
- if ( handle->id[1] ) close( handle->id[1] );
- delete handle;
- stream_.apiHandle = 0;
- }
-
- for ( int i=0; i<2; i++ ) {
- if ( stream_.userBuffer[i] ) {
- free( stream_.userBuffer[i] );
- stream_.userBuffer[i] = 0;
- }
- }
-
- if ( stream_.deviceBuffer ) {
- free( stream_.deviceBuffer );
- stream_.deviceBuffer = 0;
- }
-
- stream_.mode = UNINITIALIZED;
- stream_.state = STREAM_CLOSED;
-}
-
-void RtApiOss :: startStream()
-{
- verifyStream();
- if ( stream_.state == STREAM_RUNNING ) {
- errorText_ = "RtApiOss::startStream(): the stream is already running!";
- error( RtAudioError::WARNING );
- return;
- }
-
- MUTEX_LOCK( &stream_.mutex );
-
- #if defined( HAVE_GETTIMEOFDAY )
- gettimeofday( &stream_.lastTickTimestamp, NULL );
- #endif
-
- stream_.state = STREAM_RUNNING;
-
- // No need to do anything else here ... OSS automatically starts
- // when fed samples.
-
- MUTEX_UNLOCK( &stream_.mutex );
-
- OssHandle *handle = (OssHandle *) stream_.apiHandle;
- pthread_cond_signal( &handle->runnable );
-}
-
-void RtApiOss :: stopStream()
-{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiOss::stopStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
- }
-
- MUTEX_LOCK( &stream_.mutex );
-
- // The state might change while waiting on a mutex.
- if ( stream_.state == STREAM_STOPPED ) {
- MUTEX_UNLOCK( &stream_.mutex );
- return;
- }
-
- int result = 0;
- OssHandle *handle = (OssHandle *) stream_.apiHandle;
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
-
- // Flush the output with zeros a few times.
- char *buffer;
- int samples;
- RtAudioFormat format;
-
- if ( stream_.doConvertBuffer[0] ) {
- buffer = stream_.deviceBuffer;
- samples = stream_.bufferSize * stream_.nDeviceChannels[0];
- format = stream_.deviceFormat[0];
- }
- else {
- buffer = stream_.userBuffer[0];
- samples = stream_.bufferSize * stream_.nUserChannels[0];
- format = stream_.userFormat;
- }
-
- memset( buffer, 0, samples * formatBytes(format) );
- for ( unsigned int i=0; i<stream_.nBuffers+1; i++ ) {
- result = write( handle->id[0], buffer, samples * formatBytes(format) );
- if ( result == -1 ) {
- errorText_ = "RtApiOss::stopStream: audio write error.";
- error( RtAudioError::WARNING );
- }
- }
-
- result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
- if ( result == -1 ) {
- errorStream_ << "RtApiOss::stopStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";
- errorText_ = errorStream_.str();
- goto unlock;
- }
- handle->triggered = false;
- }
-
- if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {
- result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
- if ( result == -1 ) {
- errorStream_ << "RtApiOss::stopStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";
- errorText_ = errorStream_.str();
- goto unlock;
- }
- }
-
- unlock:
- stream_.state = STREAM_STOPPED;
- MUTEX_UNLOCK( &stream_.mutex );
-
- if ( result != -1 ) return;
- error( RtAudioError::SYSTEM_ERROR );
-}
-
-void RtApiOss :: abortStream()
-{
- verifyStream();
- if ( stream_.state == STREAM_STOPPED ) {
- errorText_ = "RtApiOss::abortStream(): the stream is already stopped!";
- error( RtAudioError::WARNING );
- return;
- }
-
- MUTEX_LOCK( &stream_.mutex );
-
- // The state might change while waiting on a mutex.
- if ( stream_.state == STREAM_STOPPED ) {
- MUTEX_UNLOCK( &stream_.mutex );
- return;
- }
-
- int result = 0;
- OssHandle *handle = (OssHandle *) stream_.apiHandle;
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
- result = ioctl( handle->id[0], SNDCTL_DSP_HALT, 0 );
- if ( result == -1 ) {
- errorStream_ << "RtApiOss::abortStream: system error stopping callback procedure on device (" << stream_.device[0] << ").";
- errorText_ = errorStream_.str();
- goto unlock;
- }
- handle->triggered = false;
- }
-
- if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && handle->id[0] != handle->id[1] ) ) {
- result = ioctl( handle->id[1], SNDCTL_DSP_HALT, 0 );
- if ( result == -1 ) {
- errorStream_ << "RtApiOss::abortStream: system error stopping input callback procedure on device (" << stream_.device[0] << ").";
- errorText_ = errorStream_.str();
- goto unlock;
- }
- }
-
- unlock:
- stream_.state = STREAM_STOPPED;
- MUTEX_UNLOCK( &stream_.mutex );
-
- if ( result != -1 ) return;
- error( RtAudioError::SYSTEM_ERROR );
-}
-
-void RtApiOss :: callbackEvent()
-{
- OssHandle *handle = (OssHandle *) stream_.apiHandle;
- if ( stream_.state == STREAM_STOPPED ) {
- MUTEX_LOCK( &stream_.mutex );
- pthread_cond_wait( &handle->runnable, &stream_.mutex );
- if ( stream_.state != STREAM_RUNNING ) {
- MUTEX_UNLOCK( &stream_.mutex );
- return;
- }
- MUTEX_UNLOCK( &stream_.mutex );
- }
-
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApiOss::callbackEvent(): the stream is closed ... this shouldn't happen!";
- error( RtAudioError::WARNING );
- return;
- }
-
- // Invoke user callback to get fresh output data.
- int doStopStream = 0;
- RtAudioCallback callback = (RtAudioCallback) stream_.callbackInfo.callback;
- double streamTime = getStreamTime();
- RtAudioStreamStatus status = 0;
- if ( stream_.mode != INPUT && handle->xrun[0] == true ) {
- status |= RTAUDIO_OUTPUT_UNDERFLOW;
- handle->xrun[0] = false;
- }
- if ( stream_.mode != OUTPUT && handle->xrun[1] == true ) {
- status |= RTAUDIO_INPUT_OVERFLOW;
- handle->xrun[1] = false;
- }
- doStopStream = callback( stream_.userBuffer[0], stream_.userBuffer[1],
- stream_.bufferSize, streamTime, status, stream_.callbackInfo.userData );
- if ( doStopStream == 2 ) {
- this->abortStream();
- return;
- }
-
- MUTEX_LOCK( &stream_.mutex );
-
- // The state might change while waiting on a mutex.
- if ( stream_.state == STREAM_STOPPED ) goto unlock;
-
- int result;
- char *buffer;
- int samples;
- RtAudioFormat format;
-
- if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
-
- // Setup parameters and do buffer conversion if necessary.
- if ( stream_.doConvertBuffer[0] ) {
- buffer = stream_.deviceBuffer;
- convertBuffer( buffer, stream_.userBuffer[0], stream_.convertInfo[0] );
- samples = stream_.bufferSize * stream_.nDeviceChannels[0];
- format = stream_.deviceFormat[0];
- }
- else {
- buffer = stream_.userBuffer[0];
- samples = stream_.bufferSize * stream_.nUserChannels[0];
- format = stream_.userFormat;
- }
-
- // Do byte swapping if necessary.
- if ( stream_.doByteSwap[0] )
- byteSwapBuffer( buffer, samples, format );
-
- if ( stream_.mode == DUPLEX && handle->triggered == false ) {
- int trig = 0;
- ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );
- result = write( handle->id[0], buffer, samples * formatBytes(format) );
- trig = PCM_ENABLE_INPUT|PCM_ENABLE_OUTPUT;
- ioctl( handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig );
- handle->triggered = true;
- }
- else
- // Write samples to device.
- result = write( handle->id[0], buffer, samples * formatBytes(format) );
-
- if ( result == -1 ) {
- // We'll assume this is an underrun, though there isn't a
- // specific means for determining that.
- handle->xrun[0] = true;
- errorText_ = "RtApiOss::callbackEvent: audio write error.";
- error( RtAudioError::WARNING );
- // Continue on to input section.
- }
- }
-
- if ( stream_.mode == INPUT || stream_.mode == DUPLEX ) {
-
- // Setup parameters.
- if ( stream_.doConvertBuffer[1] ) {
- buffer = stream_.deviceBuffer;
- samples = stream_.bufferSize * stream_.nDeviceChannels[1];
- format = stream_.deviceFormat[1];
- }
- else {
- buffer = stream_.userBuffer[1];
- samples = stream_.bufferSize * stream_.nUserChannels[1];
- format = stream_.userFormat;
- }
-
- // Read samples from device.
- result = read( handle->id[1], buffer, samples * formatBytes(format) );
-
- if ( result == -1 ) {
- // We'll assume this is an overrun, though there isn't a
- // specific means for determining that.
- handle->xrun[1] = true;
- errorText_ = "RtApiOss::callbackEvent: audio read error.";
- error( RtAudioError::WARNING );
- goto unlock;
- }
-
- // Do byte swapping if necessary.
- if ( stream_.doByteSwap[1] )
- byteSwapBuffer( buffer, samples, format );
-
- // Do buffer conversion if necessary.
- if ( stream_.doConvertBuffer[1] )
- convertBuffer( stream_.userBuffer[1], stream_.deviceBuffer, stream_.convertInfo[1] );
- }
-
- unlock:
- MUTEX_UNLOCK( &stream_.mutex );
-
- RtApi::tickStreamTime();
- if ( doStopStream == 1 ) this->stopStream();
-}
-
-static void *ossCallbackHandler( void *ptr )
-{
- CallbackInfo *info = (CallbackInfo *) ptr;
- RtApiOss *object = (RtApiOss *) info->object;
- bool *isRunning = &info->isRunning;
-
-#ifdef SCHED_RR // Undefined with some OSes (e.g. NetBSD 1.6.x with GNU Pthread)
- if (info->doRealtime) {
- std::cerr << "RtAudio oss: " <<
- (sched_getscheduler(0) == SCHED_RR ? "" : "_NOT_ ") <<
- "running realtime scheduling" << std::endl;
- }
-#endif
-
- while ( *isRunning == true ) {
- pthread_testcancel();
- object->callbackEvent();
- }
-
- pthread_exit( NULL );
-}
-
-//******************** End of __LINUX_OSS__ *********************//
-#endif
-
-
-// *************************************************** //
-//
-// Protected common (OS-independent) RtAudio methods.
-//
-// *************************************************** //
-
-// This method can be modified to control the behavior of error
-// message printing.
-void RtApi :: error( RtAudioError::Type type )
-{
- errorStream_.str(""); // clear the ostringstream
-
- RtAudioErrorCallback errorCallback = (RtAudioErrorCallback) stream_.callbackInfo.errorCallback;
- if ( errorCallback ) {
- // abortStream() can generate new error messages. Ignore them. Just keep original one.
-
- if ( firstErrorOccurred_ )
- return;
-
- firstErrorOccurred_ = true;
- const std::string errorMessage = errorText_;
-
- if ( type != RtAudioError::WARNING && stream_.state != STREAM_STOPPED) {
- stream_.callbackInfo.isRunning = false; // exit from the thread
- abortStream();
- }
-
- errorCallback( type, errorMessage );
- firstErrorOccurred_ = false;
- return;
- }
-
- if ( type == RtAudioError::WARNING && showWarnings_ == true )
- std::cerr << '\n' << errorText_ << "\n\n";
- else if ( type != RtAudioError::WARNING )
- throw( RtAudioError( errorText_, type ) );
-}
-
-void RtApi :: verifyStream()
-{
- if ( stream_.state == STREAM_CLOSED ) {
- errorText_ = "RtApi:: a stream is not open!";
- error( RtAudioError::INVALID_USE );
- }
-}
-
-void RtApi :: clearStreamInfo()
-{
- stream_.mode = UNINITIALIZED;
- stream_.state = STREAM_CLOSED;
- stream_.sampleRate = 0;
- stream_.bufferSize = 0;
- stream_.nBuffers = 0;
- stream_.userFormat = 0;
- stream_.userInterleaved = true;
- stream_.streamTime = 0.0;
- stream_.apiHandle = 0;
- stream_.deviceBuffer = 0;
- stream_.callbackInfo.callback = 0;
- stream_.callbackInfo.userData = 0;
- stream_.callbackInfo.isRunning = false;
- stream_.callbackInfo.errorCallback = 0;
- for ( int i=0; i<2; i++ ) {
- stream_.device[i] = 11111;
- stream_.doConvertBuffer[i] = false;
- stream_.deviceInterleaved[i] = true;
- stream_.doByteSwap[i] = false;
- stream_.nUserChannels[i] = 0;
- stream_.nDeviceChannels[i] = 0;
- stream_.channelOffset[i] = 0;
- stream_.deviceFormat[i] = 0;
- stream_.latency[i] = 0;
- stream_.userBuffer[i] = 0;
- stream_.convertInfo[i].channels = 0;
- stream_.convertInfo[i].inJump = 0;
- stream_.convertInfo[i].outJump = 0;
- stream_.convertInfo[i].inFormat = 0;
- stream_.convertInfo[i].outFormat = 0;
- stream_.convertInfo[i].inOffset.clear();
- stream_.convertInfo[i].outOffset.clear();
- }
-}
-
-unsigned int RtApi :: formatBytes( RtAudioFormat format )
-{
- if ( format == RTAUDIO_SINT16 )
- return 2;
- else if ( format == RTAUDIO_SINT32 || format == RTAUDIO_FLOAT32 )
- return 4;
- else if ( format == RTAUDIO_FLOAT64 )
- return 8;
- else if ( format == RTAUDIO_SINT24 )
- return 3;
- else if ( format == RTAUDIO_SINT8 )
- return 1;
-
- errorText_ = "RtApi::formatBytes: undefined format.";
- error( RtAudioError::WARNING );
-
- return 0;
-}
-
-void RtApi :: setConvertInfo( StreamMode mode, unsigned int firstChannel )
-{
- if ( mode == INPUT ) { // convert device to user buffer
- stream_.convertInfo[mode].inJump = stream_.nDeviceChannels[1];
- stream_.convertInfo[mode].outJump = stream_.nUserChannels[1];
- stream_.convertInfo[mode].inFormat = stream_.deviceFormat[1];
- stream_.convertInfo[mode].outFormat = stream_.userFormat;
- }
- else { // convert user to device buffer
- stream_.convertInfo[mode].inJump = stream_.nUserChannels[0];
- stream_.convertInfo[mode].outJump = stream_.nDeviceChannels[0];
- stream_.convertInfo[mode].inFormat = stream_.userFormat;
- stream_.convertInfo[mode].outFormat = stream_.deviceFormat[0];
- }
-
- if ( stream_.convertInfo[mode].inJump < stream_.convertInfo[mode].outJump )
- stream_.convertInfo[mode].channels = stream_.convertInfo[mode].inJump;
- else
- stream_.convertInfo[mode].channels = stream_.convertInfo[mode].outJump;
-
- // Set up the interleave/deinterleave offsets.
- if ( stream_.deviceInterleaved[mode] != stream_.userInterleaved ) {
- if ( ( mode == OUTPUT && stream_.deviceInterleaved[mode] ) ||
- ( mode == INPUT && stream_.userInterleaved ) ) {
- for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
- stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
- stream_.convertInfo[mode].outOffset.push_back( k );
- stream_.convertInfo[mode].inJump = 1;
- }
- }
- else {
- for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
- stream_.convertInfo[mode].inOffset.push_back( k );
- stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
- stream_.convertInfo[mode].outJump = 1;
- }
- }
- }
- else { // no (de)interleaving
- if ( stream_.userInterleaved ) {
- for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
- stream_.convertInfo[mode].inOffset.push_back( k );
- stream_.convertInfo[mode].outOffset.push_back( k );
- }
- }
- else {
- for ( int k=0; k<stream_.convertInfo[mode].channels; k++ ) {
- stream_.convertInfo[mode].inOffset.push_back( k * stream_.bufferSize );
- stream_.convertInfo[mode].outOffset.push_back( k * stream_.bufferSize );
- stream_.convertInfo[mode].inJump = 1;
- stream_.convertInfo[mode].outJump = 1;
- }
- }
- }
-
- // Add channel offset.
- if ( firstChannel > 0 ) {
- if ( stream_.deviceInterleaved[mode] ) {
- if ( mode == OUTPUT ) {
- for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
- stream_.convertInfo[mode].outOffset[k] += firstChannel;
- }
- else {
- for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
- stream_.convertInfo[mode].inOffset[k] += firstChannel;
- }
- }
- else {
- if ( mode == OUTPUT ) {
- for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
- stream_.convertInfo[mode].outOffset[k] += ( firstChannel * stream_.bufferSize );
- }
- else {
- for ( int k=0; k<stream_.convertInfo[mode].channels; k++ )
- stream_.convertInfo[mode].inOffset[k] += ( firstChannel * stream_.bufferSize );
- }
- }
- }
-}
-
-void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info )
-{
- // This function does format conversion, input/output channel compensation, and
- // data interleaving/deinterleaving. 24-bit integers are assumed to occupy
- // the lower three bytes of a 32-bit integer.
-
- // Clear our device buffer when in/out duplex device channels are different
- if ( outBuffer == stream_.deviceBuffer && stream_.mode == DUPLEX &&
- ( stream_.nDeviceChannels[0] < stream_.nDeviceChannels[1] ) )
- memset( outBuffer, 0, stream_.bufferSize * info.outJump * formatBytes( info.outFormat ) );
-
- int j;
- if (info.outFormat == RTAUDIO_FLOAT64) {
- Float64 scale;
- Float64 *out = (Float64 *)outBuffer;
-
- if (info.inFormat == RTAUDIO_SINT8) {
- signed char *in = (signed char *)inBuffer;
- scale = 1.0 / 127.5;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
- out[info.outOffset[j]] += 0.5;
- out[info.outOffset[j]] *= scale;
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT16) {
- Int16 *in = (Int16 *)inBuffer;
- scale = 1.0 / 32767.5;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
- out[info.outOffset[j]] += 0.5;
- out[info.outOffset[j]] *= scale;
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT24) {
- Int24 *in = (Int24 *)inBuffer;
- scale = 1.0 / 8388607.5;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Float64) (in[info.inOffset[j]].asInt());
- out[info.outOffset[j]] += 0.5;
- out[info.outOffset[j]] *= scale;
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT32) {
- Int32 *in = (Int32 *)inBuffer;
- scale = 1.0 / 2147483647.5;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
- out[info.outOffset[j]] += 0.5;
- out[info.outOffset[j]] *= scale;
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_FLOAT32) {
- Float32 *in = (Float32 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Float64) in[info.inOffset[j]];
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_FLOAT64) {
- // Channel compensation and/or (de)interleaving only.
- Float64 *in = (Float64 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = in[info.inOffset[j]];
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- }
- else if (info.outFormat == RTAUDIO_FLOAT32) {
- Float32 scale;
- Float32 *out = (Float32 *)outBuffer;
-
- if (info.inFormat == RTAUDIO_SINT8) {
- signed char *in = (signed char *)inBuffer;
- scale = (Float32) ( 1.0 / 127.5 );
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
- out[info.outOffset[j]] += 0.5;
- out[info.outOffset[j]] *= scale;
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT16) {
- Int16 *in = (Int16 *)inBuffer;
- scale = (Float32) ( 1.0 / 32767.5 );
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
- out[info.outOffset[j]] += 0.5;
- out[info.outOffset[j]] *= scale;
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT24) {
- Int24 *in = (Int24 *)inBuffer;
- scale = (Float32) ( 1.0 / 8388607.5 );
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Float32) (in[info.inOffset[j]].asInt());
- out[info.outOffset[j]] += 0.5;
- out[info.outOffset[j]] *= scale;
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT32) {
- Int32 *in = (Int32 *)inBuffer;
- scale = (Float32) ( 1.0 / 2147483647.5 );
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
- out[info.outOffset[j]] += 0.5;
- out[info.outOffset[j]] *= scale;
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_FLOAT32) {
- // Channel compensation and/or (de)interleaving only.
- Float32 *in = (Float32 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = in[info.inOffset[j]];
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_FLOAT64) {
- Float64 *in = (Float64 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Float32) in[info.inOffset[j]];
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- }
- else if (info.outFormat == RTAUDIO_SINT32) {
- Int32 *out = (Int32 *)outBuffer;
- if (info.inFormat == RTAUDIO_SINT8) {
- signed char *in = (signed char *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
- out[info.outOffset[j]] <<= 24;
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT16) {
- Int16 *in = (Int16 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int32) in[info.inOffset[j]];
- out[info.outOffset[j]] <<= 16;
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT24) {
- Int24 *in = (Int24 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int32) in[info.inOffset[j]].asInt();
- out[info.outOffset[j]] <<= 8;
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT32) {
- // Channel compensation and/or (de)interleaving only.
- Int32 *in = (Int32 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = in[info.inOffset[j]];
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_FLOAT32) {
- Float32 *in = (Float32 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_FLOAT64) {
- Float64 *in = (Float64 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 2147483647.5 - 0.5);
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- }
- else if (info.outFormat == RTAUDIO_SINT24) {
- Int24 *out = (Int24 *)outBuffer;
- if (info.inFormat == RTAUDIO_SINT8) {
- signed char *in = (signed char *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 16);
- //out[info.outOffset[j]] <<= 16;
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT16) {
- Int16 *in = (Int16 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] << 8);
- //out[info.outOffset[j]] <<= 8;
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT24) {
- // Channel compensation and/or (de)interleaving only.
- Int24 *in = (Int24 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = in[info.inOffset[j]];
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT32) {
- Int32 *in = (Int32 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] >> 8);
- //out[info.outOffset[j]] >>= 8;
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_FLOAT32) {
- Float32 *in = (Float32 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_FLOAT64) {
- Float64 *in = (Float64 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int32) (in[info.inOffset[j]] * 8388607.5 - 0.5);
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- }
- else if (info.outFormat == RTAUDIO_SINT16) {
- Int16 *out = (Int16 *)outBuffer;
- if (info.inFormat == RTAUDIO_SINT8) {
- signed char *in = (signed char *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int16) in[info.inOffset[j]];
- out[info.outOffset[j]] <<= 8;
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT16) {
- // Channel compensation and/or (de)interleaving only.
- Int16 *in = (Int16 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = in[info.inOffset[j]];
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT24) {
- Int24 *in = (Int24 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]].asInt() >> 8);
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT32) {
- Int32 *in = (Int32 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int16) ((in[info.inOffset[j]] >> 16) & 0x0000ffff);
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_FLOAT32) {
- Float32 *in = (Float32 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_FLOAT64) {
- Float64 *in = (Float64 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (Int16) (in[info.inOffset[j]] * 32767.5 - 0.5);
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- }
- else if (info.outFormat == RTAUDIO_SINT8) {
- signed char *out = (signed char *)outBuffer;
- if (info.inFormat == RTAUDIO_SINT8) {
- // Channel compensation and/or (de)interleaving only.
- signed char *in = (signed char *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = in[info.inOffset[j]];
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- if (info.inFormat == RTAUDIO_SINT16) {
- Int16 *in = (Int16 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 8) & 0x00ff);
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT24) {
- Int24 *in = (Int24 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]].asInt() >> 16);
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_SINT32) {
- Int32 *in = (Int32 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (signed char) ((in[info.inOffset[j]] >> 24) & 0x000000ff);
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_FLOAT32) {
- Float32 *in = (Float32 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- else if (info.inFormat == RTAUDIO_FLOAT64) {
- Float64 *in = (Float64 *)inBuffer;
- for (unsigned int i=0; i<stream_.bufferSize; i++) {
- for (j=0; j<info.channels; j++) {
- out[info.outOffset[j]] = (signed char) (in[info.inOffset[j]] * 127.5 - 0.5);
- }
- in += info.inJump;
- out += info.outJump;
- }
- }
- }
-}
-
-//static inline uint16_t bswap_16(uint16_t x) { return (x>>8) | (x<<8); }
-//static inline uint32_t bswap_32(uint32_t x) { return (bswap_16(x&0xffff)<<16) | (bswap_16(x>>16)); }
-//static inline uint64_t bswap_64(uint64_t x) { return (((unsigned long long)bswap_32(x&0xffffffffull))<<32) | (bswap_32(x>>32)); }
-
-void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )
-{
- char val;
- char *ptr;
-
- ptr = buffer;
- if ( format == RTAUDIO_SINT16 ) {
- for ( unsigned int i=0; i<samples; i++ ) {
- // Swap 1st and 2nd bytes.
- val = *(ptr);
- *(ptr) = *(ptr+1);
- *(ptr+1) = val;
-
- // Increment 2 bytes.
- ptr += 2;
- }
- }
- else if ( format == RTAUDIO_SINT32 ||
- format == RTAUDIO_FLOAT32 ) {
- for ( unsigned int i=0; i<samples; i++ ) {
- // Swap 1st and 4th bytes.
- val = *(ptr);
- *(ptr) = *(ptr+3);
- *(ptr+3) = val;
-
- // Swap 2nd and 3rd bytes.
- ptr += 1;
- val = *(ptr);
- *(ptr) = *(ptr+1);
- *(ptr+1) = val;
-
- // Increment 3 more bytes.
- ptr += 3;
- }
- }
- else if ( format == RTAUDIO_SINT24 ) {
- for ( unsigned int i=0; i<samples; i++ ) {
- // Swap 1st and 3rd bytes.
- val = *(ptr);
- *(ptr) = *(ptr+2);
- *(ptr+2) = val;
-
- // Increment 2 more bytes.
- ptr += 2;
- }
- }
- else if ( format == RTAUDIO_FLOAT64 ) {
- for ( unsigned int i=0; i<samples; i++ ) {
- // Swap 1st and 8th bytes
- val = *(ptr);
- *(ptr) = *(ptr+7);
- *(ptr+7) = val;
-
- // Swap 2nd and 7th bytes
- ptr += 1;
- val = *(ptr);
- *(ptr) = *(ptr+5);
- *(ptr+5) = val;
-
- // Swap 3rd and 6th bytes
- ptr += 1;
- val = *(ptr);
- *(ptr) = *(ptr+3);
- *(ptr+3) = val;
-
- // Swap 4th and 5th bytes
- ptr += 1;
- val = *(ptr);
- *(ptr) = *(ptr+1);
- *(ptr+1) = val;
-
- // Increment 5 more bytes.
- ptr += 5;
- }
- }
-}
-
- // Indentation settings for Vim and Emacs
- //
- // Local Variables:
- // c-basic-offset: 2
- // indent-tabs-mode: nil
- // End:
- //
- // vim: et sts=2 sw=2
-
+++ /dev/null
-/************************************************************************/
-/*! \class RtAudio
- \brief Realtime audio i/o C++ classes.
-
- RtAudio provides a common API (Application Programming Interface)
- for realtime audio input/output across Linux (native ALSA, Jack,
- and OSS), Macintosh OS X (CoreAudio and Jack), and Windows
- (DirectSound, ASIO and WASAPI) operating systems.
-
- RtAudio GitHub site: https://github.com/thestk/rtaudio
- RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/
-
- RtAudio: realtime audio i/o C++ classes
- Copyright (c) 2001-2019 Gary P. Scavone
-
- Permission is hereby granted, free of charge, to any person
- obtaining a copy of this software and associated documentation files
- (the "Software"), to deal in the Software without restriction,
- including without limitation the rights to use, copy, modify, merge,
- publish, distribute, sublicense, and/or sell copies of the Software,
- and to permit persons to whom the Software is furnished to do so,
- subject to the following conditions:
-
- The above copyright notice and this permission notice shall be
- included in all copies or substantial portions of the Software.
-
- Any person wishing to distribute modifications to the Software is
- asked to send the modifications to the original developer so that
- they can be incorporated into the canonical version. This is,
- however, not a binding provision of this license.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
- ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
-/************************************************************************/
-
-/*!
- \file RtAudio.h
- */
-
-#ifndef __RTAUDIO_H
-#define __RTAUDIO_H
-
-#define RTAUDIO_VERSION "5.1.0"
-
-#if defined _WIN32 || defined __CYGWIN__
- #if defined(RTAUDIO_EXPORT)
- #define RTAUDIO_DLL_PUBLIC __declspec(dllexport)
- #else
- #define RTAUDIO_DLL_PUBLIC
- #endif
-#else
- #if __GNUC__ >= 4
- #define RTAUDIO_DLL_PUBLIC __attribute__( (visibility( "default" )) )
- #else
- #define RTAUDIO_DLL_PUBLIC
- #endif
-#endif
-
-#include <string>
-#include <vector>
-#include <stdexcept>
-#include <iostream>
-
-/*! \typedef typedef unsigned long RtAudioFormat;
- \brief RtAudio data format type.
-
- Support for signed integers and floats. Audio data fed to/from an
- RtAudio stream is assumed to ALWAYS be in host byte order. The
- internal routines will automatically take care of any necessary
- byte-swapping between the host format and the soundcard. Thus,
- endian-ness is not a concern in the following format definitions.
-
- - \e RTAUDIO_SINT8: 8-bit signed integer.
- - \e RTAUDIO_SINT16: 16-bit signed integer.
- - \e RTAUDIO_SINT24: 24-bit signed integer.
- - \e RTAUDIO_SINT32: 32-bit signed integer.
- - \e RTAUDIO_FLOAT32: Normalized between plus/minus 1.0.
- - \e RTAUDIO_FLOAT64: Normalized between plus/minus 1.0.
-*/
-typedef unsigned long RtAudioFormat;
-static const RtAudioFormat RTAUDIO_SINT8 = 0x1; // 8-bit signed integer.
-static const RtAudioFormat RTAUDIO_SINT16 = 0x2; // 16-bit signed integer.
-static const RtAudioFormat RTAUDIO_SINT24 = 0x4; // 24-bit signed integer.
-static const RtAudioFormat RTAUDIO_SINT32 = 0x8; // 32-bit signed integer.
-static const RtAudioFormat RTAUDIO_FLOAT32 = 0x10; // Normalized between plus/minus 1.0.
-static const RtAudioFormat RTAUDIO_FLOAT64 = 0x20; // Normalized between plus/minus 1.0.
-
-/*! \typedef typedef unsigned long RtAudioStreamFlags;
- \brief RtAudio stream option flags.
-
- The following flags can be OR'ed together to allow a client to
- make changes to the default stream behavior:
-
- - \e RTAUDIO_NONINTERLEAVED: Use non-interleaved buffers (default = interleaved).
- - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency.
- - \e RTAUDIO_HOG_DEVICE: Attempt grab device for exclusive use.
- - \e RTAUDIO_ALSA_USE_DEFAULT: Use the "default" PCM device (ALSA only).
- - \e RTAUDIO_JACK_DONT_CONNECT: Do not automatically connect ports (JACK only).
-
- By default, RtAudio streams pass and receive audio data from the
- client in an interleaved format. By passing the
- RTAUDIO_NONINTERLEAVED flag to the openStream() function, audio
- data will instead be presented in non-interleaved buffers. In
- this case, each buffer argument in the RtAudioCallback function
- will point to a single array of data, with \c nFrames samples for
- each channel concatenated back-to-back. For example, the first
- sample of data for the second channel would be located at index \c
- nFrames (assuming the \c buffer pointer was recast to the correct
- data type for the stream).
-
- Certain audio APIs offer a number of parameters that influence the
- I/O latency of a stream. By default, RtAudio will attempt to set
- these parameters internally for robust (glitch-free) performance
- (though some APIs, like Windows DirectSound, make this difficult).
- By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream()
- function, internal stream settings will be influenced in an attempt
- to minimize stream latency, though possibly at the expense of stream
- performance.
-
- If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to
- open the input and/or output stream device(s) for exclusive use.
- Note that this is not possible with all supported audio APIs.
-
- If the RTAUDIO_SCHEDULE_REALTIME flag is set, RtAudio will attempt
- to select realtime scheduling (round-robin) for the callback thread.
-
- If the RTAUDIO_ALSA_USE_DEFAULT flag is set, RtAudio will attempt to
- open the "default" PCM device when using the ALSA API. Note that this
- will override any specified input or output device id.
-
- If the RTAUDIO_JACK_DONT_CONNECT flag is set, RtAudio will not attempt
- to automatically connect the ports of the client to the audio device.
-*/
-typedef unsigned int RtAudioStreamFlags;
-static const RtAudioStreamFlags RTAUDIO_NONINTERLEAVED = 0x1; // Use non-interleaved buffers (default = interleaved).
-static const RtAudioStreamFlags RTAUDIO_MINIMIZE_LATENCY = 0x2; // Attempt to set stream parameters for lowest possible latency.
-static const RtAudioStreamFlags RTAUDIO_HOG_DEVICE = 0x4; // Attempt grab device and prevent use by others.
-static const RtAudioStreamFlags RTAUDIO_SCHEDULE_REALTIME = 0x8; // Try to select realtime scheduling for callback thread.
-static const RtAudioStreamFlags RTAUDIO_ALSA_USE_DEFAULT = 0x10; // Use the "default" PCM device (ALSA only).
-static const RtAudioStreamFlags RTAUDIO_JACK_DONT_CONNECT = 0x20; // Do not automatically connect ports (JACK only).
-
-/*! \typedef typedef unsigned long RtAudioStreamStatus;
- \brief RtAudio stream status (over- or underflow) flags.
-
- Notification of a stream over- or underflow is indicated by a
- non-zero stream \c status argument in the RtAudioCallback function.
- The stream status can be one of the following two options,
- depending on whether the stream is open for output and/or input:
-
- - \e RTAUDIO_INPUT_OVERFLOW: Input data was discarded because of an overflow condition at the driver.
- - \e RTAUDIO_OUTPUT_UNDERFLOW: The output buffer ran low, likely producing a break in the output sound.
-*/
-typedef unsigned int RtAudioStreamStatus;
-static const RtAudioStreamStatus RTAUDIO_INPUT_OVERFLOW = 0x1; // Input data was discarded because of an overflow condition at the driver.
-static const RtAudioStreamStatus RTAUDIO_OUTPUT_UNDERFLOW = 0x2; // The output buffer ran low, likely causing a gap in the output sound.
-
-//! RtAudio callback function prototype.
-/*!
- All RtAudio clients must create a function of type RtAudioCallback
- to read and/or write data from/to the audio stream. When the
- underlying audio system is ready for new input or output data, this
- function will be invoked.
-
- \param outputBuffer For output (or duplex) streams, the client
- should write \c nFrames of audio sample frames into this
- buffer. This argument should be recast to the datatype
- specified when the stream was opened. For input-only
- streams, this argument will be NULL.
-
- \param inputBuffer For input (or duplex) streams, this buffer will
- hold \c nFrames of input audio sample frames. This
- argument should be recast to the datatype specified when the
- stream was opened. For output-only streams, this argument
- will be NULL.
-
- \param nFrames The number of sample frames of input or output
- data in the buffers. The actual buffer size in bytes is
- dependent on the data type and number of channels in use.
-
- \param streamTime The number of seconds that have elapsed since the
- stream was started.
-
- \param status If non-zero, this argument indicates a data overflow
- or underflow condition for the stream. The particular
- condition can be determined by comparison with the
- RtAudioStreamStatus flags.
-
- \param userData A pointer to optional data provided by the client
- when opening the stream (default = NULL).
-
- \return
- To continue normal stream operation, the RtAudioCallback function
- should return a value of zero. To stop the stream and drain the
- output buffer, the function should return a value of one. To abort
- the stream immediately, the client should return a value of two.
- */
-typedef int (*RtAudioCallback)( void *outputBuffer, void *inputBuffer,
- unsigned int nFrames,
- double streamTime,
- RtAudioStreamStatus status,
- void *userData );
-
-/************************************************************************/
-/*! \class RtAudioError
- \brief Exception handling class for RtAudio.
-
- The RtAudioError class is quite simple but it does allow errors to be
- "caught" by RtAudioError::Type. See the RtAudio documentation to know
- which methods can throw an RtAudioError.
-*/
-/************************************************************************/
-
-class RTAUDIO_DLL_PUBLIC RtAudioError : public std::runtime_error
-{
- public:
- //! Defined RtAudioError types.
- enum Type {
- WARNING, /*!< A non-critical error. */
- DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */
- UNSPECIFIED, /*!< The default, unspecified error type. */
- NO_DEVICES_FOUND, /*!< No devices found on system. */
- INVALID_DEVICE, /*!< An invalid device ID was specified. */
- MEMORY_ERROR, /*!< An error occured during memory allocation. */
- INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */
- INVALID_USE, /*!< The function was called incorrectly. */
- DRIVER_ERROR, /*!< A system driver error occured. */
- SYSTEM_ERROR, /*!< A system error occured. */
- THREAD_ERROR /*!< A thread error occured. */
- };
-
- //! The constructor.
- RtAudioError( const std::string& message,
- Type type = RtAudioError::UNSPECIFIED )
- : std::runtime_error(message), type_(type) {}
-
- //! Prints thrown error message to stderr.
- virtual void printMessage( void ) const
- { std::cerr << '\n' << what() << "\n\n"; }
-
- //! Returns the thrown error message type.
- virtual const Type& getType(void) const { return type_; }
-
- //! Returns the thrown error message string.
- virtual const std::string getMessage(void) const
- { return std::string(what()); }
-
- protected:
- Type type_;
-};
-
-//! RtAudio error callback function prototype.
-/*!
- \param type Type of error.
- \param errorText Error description.
- */
-typedef void (*RtAudioErrorCallback)( RtAudioError::Type type, const std::string &errorText );
-
-// **************************************************************** //
-//
-// RtAudio class declaration.
-//
-// RtAudio is a "controller" used to select an available audio i/o
-// interface. It presents a common API for the user to call but all
-// functionality is implemented by the class RtApi and its
-// subclasses. RtAudio creates an instance of an RtApi subclass
-// based on the user's API choice. If no choice is made, RtAudio
-// attempts to make a "logical" API selection.
-//
-// **************************************************************** //
-
-class RtApi;
-
-class RTAUDIO_DLL_PUBLIC RtAudio
-{
- public:
-
- //! Audio API specifier arguments.
- enum Api {
- UNSPECIFIED, /*!< Search for a working compiled API. */
- LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */
- LINUX_PULSE, /*!< The Linux PulseAudio API. */
- LINUX_OSS, /*!< The Linux Open Sound System API. */
- UNIX_JACK, /*!< The Jack Low-Latency Audio Server API. */
- MACOSX_CORE, /*!< Macintosh OS-X Core Audio API. */
- WINDOWS_WASAPI, /*!< The Microsoft WASAPI API. */
- WINDOWS_ASIO, /*!< The Steinberg Audio Stream I/O API. */
- WINDOWS_DS, /*!< The Microsoft DirectSound API. */
- RTAUDIO_DUMMY, /*!< A compilable but non-functional API. */
- NUM_APIS /*!< Number of values in this enum. */
- };
-
- //! The public device information structure for returning queried values.
- struct DeviceInfo {
- bool probed; /*!< true if the device capabilities were successfully probed. */
- std::string name; /*!< Character string device identifier. */
- unsigned int outputChannels; /*!< Maximum output channels supported by device. */
- unsigned int inputChannels; /*!< Maximum input channels supported by device. */
- unsigned int duplexChannels; /*!< Maximum simultaneous input/output channels supported by device. */
- bool isDefaultOutput; /*!< true if this is the default output device. */
- bool isDefaultInput; /*!< true if this is the default input device. */
- std::vector<unsigned int> sampleRates; /*!< Supported sample rates (queried from list of standard rates). */
- unsigned int preferredSampleRate; /*!< Preferred sample rate, e.g. for WASAPI the system sample rate. */
- RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */
-
- // Default constructor.
- DeviceInfo()
- :probed(false), outputChannels(0), inputChannels(0), duplexChannels(0),
- isDefaultOutput(false), isDefaultInput(false), preferredSampleRate(0), nativeFormats(0) {}
- };
-
- //! The structure for specifying input or ouput stream parameters.
- struct StreamParameters {
- unsigned int deviceId; /*!< Device index (0 to getDeviceCount() - 1). */
- unsigned int nChannels; /*!< Number of channels. */
- unsigned int firstChannel; /*!< First channel index on device (default = 0). */
-
- // Default constructor.
- StreamParameters()
- : deviceId(0), nChannels(0), firstChannel(0) {}
- };
-
- //! The structure for specifying stream options.
- /*!
- The following flags can be OR'ed together to allow a client to
- make changes to the default stream behavior:
-
- - \e RTAUDIO_NONINTERLEAVED: Use non-interleaved buffers (default = interleaved).
- - \e RTAUDIO_MINIMIZE_LATENCY: Attempt to set stream parameters for lowest possible latency.
- - \e RTAUDIO_HOG_DEVICE: Attempt grab device for exclusive use.
- - \e RTAUDIO_SCHEDULE_REALTIME: Attempt to select realtime scheduling for callback thread.
- - \e RTAUDIO_ALSA_USE_DEFAULT: Use the "default" PCM device (ALSA only).
-
- By default, RtAudio streams pass and receive audio data from the
- client in an interleaved format. By passing the
- RTAUDIO_NONINTERLEAVED flag to the openStream() function, audio
- data will instead be presented in non-interleaved buffers. In
- this case, each buffer argument in the RtAudioCallback function
- will point to a single array of data, with \c nFrames samples for
- each channel concatenated back-to-back. For example, the first
- sample of data for the second channel would be located at index \c
- nFrames (assuming the \c buffer pointer was recast to the correct
- data type for the stream).
-
- Certain audio APIs offer a number of parameters that influence the
- I/O latency of a stream. By default, RtAudio will attempt to set
- these parameters internally for robust (glitch-free) performance
- (though some APIs, like Windows DirectSound, make this difficult).
- By passing the RTAUDIO_MINIMIZE_LATENCY flag to the openStream()
- function, internal stream settings will be influenced in an attempt
- to minimize stream latency, though possibly at the expense of stream
- performance.
-
- If the RTAUDIO_HOG_DEVICE flag is set, RtAudio will attempt to
- open the input and/or output stream device(s) for exclusive use.
- Note that this is not possible with all supported audio APIs.
-
- If the RTAUDIO_SCHEDULE_REALTIME flag is set, RtAudio will attempt
- to select realtime scheduling (round-robin) for the callback thread.
- The \c priority parameter will only be used if the RTAUDIO_SCHEDULE_REALTIME
- flag is set. It defines the thread's realtime priority.
-
- If the RTAUDIO_ALSA_USE_DEFAULT flag is set, RtAudio will attempt to
- open the "default" PCM device when using the ALSA API. Note that this
- will override any specified input or output device id.
-
- The \c numberOfBuffers parameter can be used to control stream
- latency in the Windows DirectSound, Linux OSS, and Linux Alsa APIs
- only. A value of two is usually the smallest allowed. Larger
- numbers can potentially result in more robust stream performance,
- though likely at the cost of stream latency. The value set by the
- user is replaced during execution of the RtAudio::openStream()
- function by the value actually used by the system.
-
- The \c streamName parameter can be used to set the client name
- when using the Jack API. By default, the client name is set to
- RtApiJack. However, if you wish to create multiple instances of
- RtAudio with Jack, each instance must have a unique client name.
- */
- struct StreamOptions {
- RtAudioStreamFlags flags; /*!< A bit-mask of stream flags (RTAUDIO_NONINTERLEAVED, RTAUDIO_MINIMIZE_LATENCY, RTAUDIO_HOG_DEVICE, RTAUDIO_ALSA_USE_DEFAULT). */
- unsigned int numberOfBuffers; /*!< Number of stream buffers. */
- std::string streamName; /*!< A stream name (currently used only in Jack). */
- int priority; /*!< Scheduling priority of callback thread (only used with flag RTAUDIO_SCHEDULE_REALTIME). */
-
- // Default constructor.
- StreamOptions()
- : flags(0), numberOfBuffers(0), priority(0) {}
- };
-
- //! A static function to determine the current RtAudio version.
- static std::string getVersion( void );
-
- //! A static function to determine the available compiled audio APIs.
- /*!
- The values returned in the std::vector can be compared against
- the enumerated list values. Note that there can be more than one
- API compiled for certain operating systems.
- */
- static void getCompiledApi( std::vector<RtAudio::Api> &apis );
-
- //! Return the name of a specified compiled audio API.
- /*!
- This obtains a short lower-case name used for identification purposes.
- This value is guaranteed to remain identical across library versions.
- If the API is unknown, this function will return the empty string.
- */
- static std::string getApiName( RtAudio::Api api );
-
- //! Return the display name of a specified compiled audio API.
- /*!
- This obtains a long name used for display purposes.
- If the API is unknown, this function will return the empty string.
- */
- static std::string getApiDisplayName( RtAudio::Api api );
-
- //! Return the compiled audio API having the given name.
- /*!
- A case insensitive comparison will check the specified name
- against the list of compiled APIs, and return the one which
- matches. On failure, the function returns UNSPECIFIED.
- */
- static RtAudio::Api getCompiledApiByName( const std::string &name );
-
- //! The class constructor.
- /*!
- The constructor performs minor initialization tasks. An exception
- can be thrown if no API support is compiled.
-
- If no API argument is specified and multiple API support has been
- compiled, the default order of use is JACK, ALSA, OSS (Linux
- systems) and ASIO, DS (Windows systems).
- */
- RtAudio( RtAudio::Api api=UNSPECIFIED );
-
- //! The destructor.
- /*!
- If a stream is running or open, it will be stopped and closed
- automatically.
- */
- ~RtAudio();
-
- //! Returns the audio API specifier for the current instance of RtAudio.
- RtAudio::Api getCurrentApi( void );
-
- //! A public function that queries for the number of audio devices available.
- /*!
- This function performs a system query of available devices each time it
- is called, thus supporting devices connected \e after instantiation. If
- a system error occurs during processing, a warning will be issued.
- */
- unsigned int getDeviceCount( void );
-
- //! Return an RtAudio::DeviceInfo structure for a specified device number.
- /*!
-
- Any device integer between 0 and getDeviceCount() - 1 is valid.
- If an invalid argument is provided, an RtAudioError (type = INVALID_USE)
- will be thrown. If a device is busy or otherwise unavailable, the
- structure member "probed" will have a value of "false" and all
- other members are undefined. If the specified device is the
- current default input or output device, the corresponding
- "isDefault" member will have a value of "true".
- */
- RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
-
- //! A function that returns the index of the default output device.
- /*!
- If the underlying audio API does not provide a "default
- device", or if no devices are available, the return value will be
- 0. Note that this is a valid device identifier and it is the
- client's responsibility to verify that a device is available
- before attempting to open a stream.
- */
- unsigned int getDefaultOutputDevice( void );
-
- //! A function that returns the index of the default input device.
- /*!
- If the underlying audio API does not provide a "default
- device", or if no devices are available, the return value will be
- 0. Note that this is a valid device identifier and it is the
- client's responsibility to verify that a device is available
- before attempting to open a stream.
- */
- unsigned int getDefaultInputDevice( void );
-
- //! A public function for opening a stream with the specified parameters.
- /*!
- An RtAudioError (type = SYSTEM_ERROR) is thrown if a stream cannot be
- opened with the specified parameters or an error occurs during
- processing. An RtAudioError (type = INVALID_USE) is thrown if any
- invalid device ID or channel number parameters are specified.
-
- \param outputParameters Specifies output stream parameters to use
- when opening a stream, including a device ID, number of channels,
- and starting channel number. For input-only streams, this
- argument should be NULL. The device ID is an index value between
- 0 and getDeviceCount() - 1.
- \param inputParameters Specifies input stream parameters to use
- when opening a stream, including a device ID, number of channels,
- and starting channel number. For output-only streams, this
- argument should be NULL. The device ID is an index value between
- 0 and getDeviceCount() - 1.
- \param format An RtAudioFormat specifying the desired sample data format.
- \param sampleRate The desired sample rate (sample frames per second).
- \param *bufferFrames A pointer to a value indicating the desired
- internal buffer size in sample frames. The actual value
- used by the device is returned via the same pointer. A
- value of zero can be specified, in which case the lowest
- allowable value is determined.
- \param callback A client-defined function that will be invoked
- when input data is available and/or output data is needed.
- \param userData An optional pointer to data that can be accessed
- from within the callback function.
- \param options An optional pointer to a structure containing various
- global stream options, including a list of OR'ed RtAudioStreamFlags
- and a suggested number of stream buffers that can be used to
- control stream latency. More buffers typically result in more
- robust performance, though at a cost of greater latency. If a
- value of zero is specified, a system-specific median value is
- chosen. If the RTAUDIO_MINIMIZE_LATENCY flag bit is set, the
- lowest allowable value is used. The actual value used is
- returned via the structure argument. The parameter is API dependent.
- \param errorCallback A client-defined function that will be invoked
- when an error has occured.
- */
- void openStream( RtAudio::StreamParameters *outputParameters,
- RtAudio::StreamParameters *inputParameters,
- RtAudioFormat format, unsigned int sampleRate,
- unsigned int *bufferFrames, RtAudioCallback callback,
- void *userData = NULL, RtAudio::StreamOptions *options = NULL, RtAudioErrorCallback errorCallback = NULL );
-
- //! A function that closes a stream and frees any associated stream memory.
- /*!
- If a stream is not open, this function issues a warning and
- returns (no exception is thrown).
- */
- void closeStream( void );
-
- //! A function that starts a stream.
- /*!
- An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs
- during processing. An RtAudioError (type = INVALID_USE) is thrown if a
- stream is not open. A warning is issued if the stream is already
- running.
- */
- void startStream( void );
-
- //! Stop a stream, allowing any samples remaining in the output queue to be played.
- /*!
- An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs
- during processing. An RtAudioError (type = INVALID_USE) is thrown if a
- stream is not open. A warning is issued if the stream is already
- stopped.
- */
- void stopStream( void );
-
- //! Stop a stream, discarding any samples remaining in the input/output queue.
- /*!
- An RtAudioError (type = SYSTEM_ERROR) is thrown if an error occurs
- during processing. An RtAudioError (type = INVALID_USE) is thrown if a
- stream is not open. A warning is issued if the stream is already
- stopped.
- */
- void abortStream( void );
-
- //! Returns true if a stream is open and false if not.
- bool isStreamOpen( void ) const;
-
- //! Returns true if the stream is running and false if it is stopped or not open.
- bool isStreamRunning( void ) const;
-
- //! Returns the number of elapsed seconds since the stream was started.
- /*!
- If a stream is not open, an RtAudioError (type = INVALID_USE) will be thrown.
- */
- double getStreamTime( void );
-
- //! Set the stream time to a time in seconds greater than or equal to 0.0.
- /*!
- If a stream is not open, an RtAudioError (type = INVALID_USE) will be thrown.
- */
- void setStreamTime( double time );
-
- //! Returns the internal stream latency in sample frames.
- /*!
- The stream latency refers to delay in audio input and/or output
- caused by internal buffering by the audio system and/or hardware.
- For duplex streams, the returned value will represent the sum of
- the input and output latencies. If a stream is not open, an
- RtAudioError (type = INVALID_USE) will be thrown. If the API does not
- report latency, the return value will be zero.
- */
- long getStreamLatency( void );
-
- //! Returns actual sample rate in use by the stream.
- /*!
- On some systems, the sample rate used may be slightly different
- than that specified in the stream parameters. If a stream is not
- open, an RtAudioError (type = INVALID_USE) will be thrown.
- */
- unsigned int getStreamSampleRate( void );
-
- //! Specify whether warning messages should be printed to stderr.
- void showWarnings( bool value = true );
-
-#if defined(__UNIX_JACK__)
- void* HACK__getJackClient();
-#endif
-
- protected:
-
- void openRtApi( RtAudio::Api api );
- RtApi *rtapi_;
-};
-
-// Operating system dependent thread functionality.
-#if defined(__WINDOWS_DS__) || defined(__WINDOWS_ASIO__) || defined(__WINDOWS_WASAPI__)
-
- #ifndef NOMINMAX
- #define NOMINMAX
- #endif
- #include <windows.h>
- #include <process.h>
- #include <stdint.h>
-
- typedef uintptr_t ThreadHandle;
- typedef CRITICAL_SECTION StreamMutex;
-
-#elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)
- // Using pthread library for various flavors of unix.
- #include <pthread.h>
-
- typedef pthread_t ThreadHandle;
- typedef pthread_mutex_t StreamMutex;
-
-#else // Setup for "dummy" behavior
-
- #define __RTAUDIO_DUMMY__
- typedef int ThreadHandle;
- typedef int StreamMutex;
-
-#endif
-
-// This global structure type is used to pass callback information
-// between the private RtAudio stream structure and global callback
-// handling functions.
-struct CallbackInfo {
- void *object; // Used as a "this" pointer.
- ThreadHandle thread;
- void *callback;
- void *userData;
- void *errorCallback;
- void *apiInfo; // void pointer for API specific callback information
- bool isRunning;
- bool doRealtime;
- int priority;
-
- // Default constructor.
- CallbackInfo()
- :object(0), callback(0), userData(0), errorCallback(0), apiInfo(0), isRunning(false), doRealtime(false), priority(0) {}
-};
-
-// **************************************************************** //
-//
-// RtApi class declaration.
-//
-// Subclasses of RtApi contain all API- and OS-specific code necessary
-// to fully implement the RtAudio API.
-//
-// Note that RtApi is an abstract base class and cannot be
-// explicitly instantiated. The class RtAudio will create an
-// instance of an RtApi subclass (RtApiOss, RtApiAlsa,
-// RtApiJack, RtApiCore, RtApiDs, or RtApiAsio).
-//
-// **************************************************************** //
-
-#pragma pack(push, 1)
-class S24 {
-
- protected:
- unsigned char c3[3];
-
- public:
- S24() {}
-
- S24& operator = ( const int& i ) {
- c3[0] = (i & 0x000000ff);
- c3[1] = (i & 0x0000ff00) >> 8;
- c3[2] = (i & 0x00ff0000) >> 16;
- return *this;
- }
-
- S24( const double& d ) { *this = (int) d; }
- S24( const float& f ) { *this = (int) f; }
- S24( const signed short& s ) { *this = (int) s; }
- S24( const char& c ) { *this = (int) c; }
-
- int asInt() {
- int i = c3[0] | (c3[1] << 8) | (c3[2] << 16);
- if (i & 0x800000) i |= ~0xffffff;
- return i;
- }
-};
-#pragma pack(pop)
-
-#if defined( HAVE_GETTIMEOFDAY )
- #include <sys/time.h>
-#endif
-
-#include <sstream>
-
-class RTAUDIO_DLL_PUBLIC RtApi
-{
-friend RtAudio; // HACK
-
-public:
-
- RtApi();
- virtual ~RtApi();
- virtual RtAudio::Api getCurrentApi( void ) = 0;
- virtual unsigned int getDeviceCount( void ) = 0;
- virtual RtAudio::DeviceInfo getDeviceInfo( unsigned int device ) = 0;
- virtual unsigned int getDefaultInputDevice( void );
- virtual unsigned int getDefaultOutputDevice( void );
- void openStream( RtAudio::StreamParameters *outputParameters,
- RtAudio::StreamParameters *inputParameters,
- RtAudioFormat format, unsigned int sampleRate,
- unsigned int *bufferFrames, RtAudioCallback callback,
- void *userData, RtAudio::StreamOptions *options,
- RtAudioErrorCallback errorCallback );
- virtual void closeStream( void );
- virtual void startStream( void ) = 0;
- virtual void stopStream( void ) = 0;
- virtual void abortStream( void ) = 0;
- long getStreamLatency( void );
- unsigned int getStreamSampleRate( void );
- virtual double getStreamTime( void );
- virtual void setStreamTime( double time );
- bool isStreamOpen( void ) const { return stream_.state != STREAM_CLOSED; }
- bool isStreamRunning( void ) const { return stream_.state == STREAM_RUNNING; }
- void showWarnings( bool value ) { showWarnings_ = value; }
-
-
-protected:
-
- static const unsigned int MAX_SAMPLE_RATES;
- static const unsigned int SAMPLE_RATES[];
-
- enum { FAILURE, SUCCESS };
-
- enum StreamState {
- STREAM_STOPPED,
- STREAM_STOPPING,
- STREAM_RUNNING,
- STREAM_CLOSED = -50
- };
-
- enum StreamMode {
- OUTPUT,
- INPUT,
- DUPLEX,
- UNINITIALIZED = -75
- };
-
- // A protected structure used for buffer conversion.
- struct ConvertInfo {
- int channels;
- int inJump, outJump;
- RtAudioFormat inFormat, outFormat;
- std::vector<int> inOffset;
- std::vector<int> outOffset;
- };
-
- // A protected structure for audio streams.
- struct RtApiStream {
- unsigned int device[2]; // Playback and record, respectively.
- void *apiHandle; // void pointer for API specific stream handle information
- StreamMode mode; // OUTPUT, INPUT, or DUPLEX.
- StreamState state; // STOPPED, RUNNING, or CLOSED
- char *userBuffer[2]; // Playback and record, respectively.
- char *deviceBuffer;
- bool doConvertBuffer[2]; // Playback and record, respectively.
- bool userInterleaved;
- bool deviceInterleaved[2]; // Playback and record, respectively.
- bool doByteSwap[2]; // Playback and record, respectively.
- unsigned int sampleRate;
- unsigned int bufferSize;
- unsigned int nBuffers;
- unsigned int nUserChannels[2]; // Playback and record, respectively.
- unsigned int nDeviceChannels[2]; // Playback and record channels, respectively.
- unsigned int channelOffset[2]; // Playback and record, respectively.
- unsigned long latency[2]; // Playback and record, respectively.
- RtAudioFormat userFormat;
- RtAudioFormat deviceFormat[2]; // Playback and record, respectively.
- StreamMutex mutex;
- CallbackInfo callbackInfo;
- ConvertInfo convertInfo[2];
- double streamTime; // Number of elapsed seconds since the stream started.
-
-#if defined(HAVE_GETTIMEOFDAY)
- struct timeval lastTickTimestamp;
-#endif
-
- RtApiStream()
- :apiHandle(0), deviceBuffer(0) { device[0] = 11111; device[1] = 11111; }
- };
-
- typedef S24 Int24;
- typedef signed short Int16;
- typedef signed int Int32;
- typedef float Float32;
- typedef double Float64;
-
- std::ostringstream errorStream_;
- std::string errorText_;
- bool showWarnings_;
- RtApiStream stream_;
- bool firstErrorOccurred_;
-
- /*!
- Protected, api-specific method that attempts to open a device
- with the given parameters. This function MUST be implemented by
- all subclasses. If an error is encountered during the probe, a
- "warning" message is reported and FAILURE is returned. A
- successful probe is indicated by a return value of SUCCESS.
- */
- virtual bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
- RtAudioFormat format, unsigned int *bufferSize,
- RtAudio::StreamOptions *options );
-
- //! A protected function used to increment the stream time.
- void tickStreamTime( void );
-
- //! Protected common method to clear an RtApiStream structure.
- void clearStreamInfo();
-
- /*!
- Protected common method that throws an RtAudioError (type =
- INVALID_USE) if a stream is not open.
- */
- void verifyStream( void );
-
- //! Protected common error method to allow global control over error handling.
- void error( RtAudioError::Type type );
-
- /*!
- Protected method used to perform format, channel number, and/or interleaving
- conversions between the user and device buffers.
- */
- void convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info );
-
- //! Protected common method used to perform byte-swapping on buffers.
- void byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format );
-
- //! Protected common method that returns the number of bytes for a given format.
- unsigned int formatBytes( RtAudioFormat format );
-
- //! Protected common method that sets up the parameters for buffer conversion.
- void setConvertInfo( StreamMode mode, unsigned int firstChannel );
-};
-
-// **************************************************************** //
-//
-// Inline RtAudio definitions.
-//
-// **************************************************************** //
-
-inline RtAudio::Api RtAudio :: getCurrentApi( void ) { return rtapi_->getCurrentApi(); }
-inline unsigned int RtAudio :: getDeviceCount( void ) { return rtapi_->getDeviceCount(); }
-inline RtAudio::DeviceInfo RtAudio :: getDeviceInfo( unsigned int device ) { return rtapi_->getDeviceInfo( device ); }
-inline unsigned int RtAudio :: getDefaultInputDevice( void ) { return rtapi_->getDefaultInputDevice(); }
-inline unsigned int RtAudio :: getDefaultOutputDevice( void ) { return rtapi_->getDefaultOutputDevice(); }
-inline void RtAudio :: closeStream( void ) { return rtapi_->closeStream(); }
-inline void RtAudio :: startStream( void ) { return rtapi_->startStream(); }
-inline void RtAudio :: stopStream( void ) { return rtapi_->stopStream(); }
-inline void RtAudio :: abortStream( void ) { return rtapi_->abortStream(); }
-inline bool RtAudio :: isStreamOpen( void ) const { return rtapi_->isStreamOpen(); }
-inline bool RtAudio :: isStreamRunning( void ) const { return rtapi_->isStreamRunning(); }
-inline long RtAudio :: getStreamLatency( void ) { return rtapi_->getStreamLatency(); }
-inline unsigned int RtAudio :: getStreamSampleRate( void ) { return rtapi_->getStreamSampleRate(); }
-inline double RtAudio :: getStreamTime( void ) { return rtapi_->getStreamTime(); }
-inline void RtAudio :: setStreamTime( double time ) { return rtapi_->setStreamTime( time ); }
-inline void RtAudio :: showWarnings( bool value ) { rtapi_->showWarnings( value ); }
-
-// RtApi Subclass prototypes.
-
-#if defined(__MACOSX_CORE__)
-
-#include <CoreAudio/AudioHardware.h>
-
-class RtApiCore: public RtApi
-{
-public:
-
- RtApiCore();
- ~RtApiCore();
- RtAudio::Api getCurrentApi( void ) { return RtAudio::MACOSX_CORE; }
- unsigned int getDeviceCount( void );
- RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
- unsigned int getDefaultOutputDevice( void );
- unsigned int getDefaultInputDevice( void );
- void closeStream( void );
- void startStream( void );
- void stopStream( void );
- void abortStream( void );
-
- // This function is intended for internal use only. It must be
- // public because it is called by the internal callback handler,
- // which is not a member of RtAudio. External use of this function
- // will most likely produce highly undesireable results!
- bool callbackEvent( AudioDeviceID deviceId,
- const AudioBufferList *inBufferList,
- const AudioBufferList *outBufferList );
-
- private:
-
- bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
- RtAudioFormat format, unsigned int *bufferSize,
- RtAudio::StreamOptions *options );
- static const char* getErrorCode( OSStatus code );
-};
-
-#endif
-
-#if defined(__UNIX_JACK__)
-
-class RtApiJack: public RtApi
-{
-public:
-
- RtApiJack();
- ~RtApiJack();
- RtAudio::Api getCurrentApi( void ) { return RtAudio::UNIX_JACK; }
- unsigned int getDeviceCount( void );
- RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
- void closeStream( void );
- void startStream( void );
- void stopStream( void );
- void abortStream( void );
-
- // This function is intended for internal use only. It must be
- // public because it is called by the internal callback handler,
- // which is not a member of RtAudio. External use of this function
- // will most likely produce highly undesireable results!
- bool callbackEvent( unsigned long nframes );
-
- private:
-
- bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
- RtAudioFormat format, unsigned int *bufferSize,
- RtAudio::StreamOptions *options );
-
- bool shouldAutoconnect_;
-};
-
-#endif
-
-#if defined(__WINDOWS_ASIO__)
-
-class RtApiAsio: public RtApi
-{
-public:
-
- RtApiAsio();
- ~RtApiAsio();
- RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_ASIO; }
- unsigned int getDeviceCount( void );
- RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
- void closeStream( void );
- void startStream( void );
- void stopStream( void );
- void abortStream( void );
-
- // This function is intended for internal use only. It must be
- // public because it is called by the internal callback handler,
- // which is not a member of RtAudio. External use of this function
- // will most likely produce highly undesireable results!
- bool callbackEvent( long bufferIndex );
-
- private:
-
- std::vector<RtAudio::DeviceInfo> devices_;
- void saveDeviceInfo( void );
- bool coInitialized_;
- bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
- RtAudioFormat format, unsigned int *bufferSize,
- RtAudio::StreamOptions *options );
-};
-
-#endif
-
-#if defined(__WINDOWS_DS__)
-
-class RtApiDs: public RtApi
-{
-public:
-
- RtApiDs();
- ~RtApiDs();
- RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_DS; }
- unsigned int getDeviceCount( void );
- unsigned int getDefaultOutputDevice( void );
- unsigned int getDefaultInputDevice( void );
- RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
- void closeStream( void );
- void startStream( void );
- void stopStream( void );
- void abortStream( void );
-
- // This function is intended for internal use only. It must be
- // public because it is called by the internal callback handler,
- // which is not a member of RtAudio. External use of this function
- // will most likely produce highly undesireable results!
- void callbackEvent( void );
-
- private:
-
- bool coInitialized_;
- bool buffersRolling;
- long duplexPrerollBytes;
- std::vector<struct DsDevice> dsDevices;
- bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
- RtAudioFormat format, unsigned int *bufferSize,
- RtAudio::StreamOptions *options );
-};
-
-#endif
-
-#if defined(__WINDOWS_WASAPI__)
-
-struct IMMDeviceEnumerator;
-
-class RtApiWasapi : public RtApi
-{
-public:
- RtApiWasapi();
- virtual ~RtApiWasapi();
-
- RtAudio::Api getCurrentApi( void ) { return RtAudio::WINDOWS_WASAPI; }
- unsigned int getDeviceCount( void );
- RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
- unsigned int getDefaultOutputDevice( void );
- unsigned int getDefaultInputDevice( void );
- void closeStream( void );
- void startStream( void );
- void stopStream( void );
- void abortStream( void );
-
-private:
- bool coInitialized_;
- IMMDeviceEnumerator* deviceEnumerator_;
-
- bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
- RtAudioFormat format, unsigned int* bufferSize,
- RtAudio::StreamOptions* options );
-
- static DWORD WINAPI runWasapiThread( void* wasapiPtr );
- static DWORD WINAPI stopWasapiThread( void* wasapiPtr );
- static DWORD WINAPI abortWasapiThread( void* wasapiPtr );
- void wasapiThread();
-};
-
-#endif
-
-#if defined(__LINUX_ALSA__)
-
-class RtApiAlsa: public RtApi
-{
-public:
-
- RtApiAlsa();
- ~RtApiAlsa();
- RtAudio::Api getCurrentApi() { return RtAudio::LINUX_ALSA; }
- unsigned int getDeviceCount( void );
- RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
- void closeStream( void );
- void startStream( void );
- void stopStream( void );
- void abortStream( void );
-
- // This function is intended for internal use only. It must be
- // public because it is called by the internal callback handler,
- // which is not a member of RtAudio. External use of this function
- // will most likely produce highly undesireable results!
- void callbackEvent( void );
-
- private:
-
- std::vector<RtAudio::DeviceInfo> devices_;
- void saveDeviceInfo( void );
- bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
- RtAudioFormat format, unsigned int *bufferSize,
- RtAudio::StreamOptions *options );
-};
-
-#endif
-
-#if defined(__LINUX_PULSE__)
-
-class RtApiPulse: public RtApi
-{
-public:
- ~RtApiPulse();
- RtAudio::Api getCurrentApi() { return RtAudio::LINUX_PULSE; }
- unsigned int getDeviceCount( void );
- RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
- void closeStream( void );
- void startStream( void );
- void stopStream( void );
- void abortStream( void );
-
- // This function is intended for internal use only. It must be
- // public because it is called by the internal callback handler,
- // which is not a member of RtAudio. External use of this function
- // will most likely produce highly undesireable results!
- void callbackEvent( void );
-
- private:
-
- std::vector<RtAudio::DeviceInfo> devices_;
- void saveDeviceInfo( void );
- bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
- RtAudioFormat format, unsigned int *bufferSize,
- RtAudio::StreamOptions *options );
-};
-
-#endif
-
-#if defined(__LINUX_OSS__)
-
-class RtApiOss: public RtApi
-{
-public:
-
- RtApiOss();
- ~RtApiOss();
- RtAudio::Api getCurrentApi() { return RtAudio::LINUX_OSS; }
- unsigned int getDeviceCount( void );
- RtAudio::DeviceInfo getDeviceInfo( unsigned int device );
- void closeStream( void );
- void startStream( void );
- void stopStream( void );
- void abortStream( void );
-
- // This function is intended for internal use only. It must be
- // public because it is called by the internal callback handler,
- // which is not a member of RtAudio. External use of this function
- // will most likely produce highly undesireable results!
- void callbackEvent( void );
-
- private:
-
- bool probeDeviceOpen( unsigned int device, StreamMode mode, unsigned int channels,
- unsigned int firstChannel, unsigned int sampleRate,
- RtAudioFormat format, unsigned int *bufferSize,
- RtAudio::StreamOptions *options );
-};
-
-#endif
-
-#if defined(__RTAUDIO_DUMMY__)
-
-class RtApiDummy: public RtApi
-{
-public:
-
- RtApiDummy() { errorText_ = "RtApiDummy: This class provides no functionality."; error( RtAudioError::WARNING ); }
- RtAudio::Api getCurrentApi( void ) { return RtAudio::RTAUDIO_DUMMY; }
- unsigned int getDeviceCount( void ) { return 0; }
- RtAudio::DeviceInfo getDeviceInfo( unsigned int /*device*/ ) { RtAudio::DeviceInfo info; return info; }
- void closeStream( void ) {}
- void startStream( void ) {}
- void stopStream( void ) {}
- void abortStream( void ) {}
-
- private:
-
- bool probeDeviceOpen( unsigned int /*device*/, StreamMode /*mode*/, unsigned int /*channels*/,
- unsigned int /*firstChannel*/, unsigned int /*sampleRate*/,
- RtAudioFormat /*format*/, unsigned int * /*bufferSize*/,
- RtAudio::StreamOptions * /*options*/ ) { return false; }
-};
-
-#endif
-
-#endif
-
-// Indentation settings for Vim and Emacs
-//
-// Local Variables:
-// c-basic-offset: 2
-// indent-tabs-mode: nil
-// End:
-//
-// vim: et sts=2 sw=2
+++ /dev/null
-/*\r
- Steinberg Audio Stream I/O API\r
- (c) 1996, Steinberg Soft- und Hardware GmbH\r
-\r
- asio.cpp\r
- \r
- asio functions entries which translate the\r
- asio interface to the asiodrvr class methods\r
-*/ \r
- \r
-#include <string.h>\r
-#include "asiosys.h" // platform definition\r
-#include "asio.h"\r
-\r
-#if MAC\r
-#include "asiodrvr.h"\r
-\r
-#pragma export on\r
-\r
-AsioDriver *theAsioDriver = 0;\r
-\r
-extern "C"\r
-{\r
-\r
-long main()\r
-{\r
- return 'ASIO';\r
-}\r
-\r
-#elif WINDOWS\r
-\r
-#include "windows.h"\r
-#include "iasiodrv.h"\r
-#include "asiodrivers.h"\r
-\r
-IASIO *theAsioDriver = 0;\r
-extern AsioDrivers *asioDrivers;\r
-\r
-#elif SGI || SUN || BEOS || LINUX\r
-#include "asiodrvr.h"\r
-static AsioDriver *theAsioDriver = 0;\r
-#endif\r
-\r
-//-----------------------------------------------------------------------------------------------------\r
-ASIOError ASIOInit(ASIODriverInfo *info)\r
-{\r
-#if MAC || SGI || SUN || BEOS || LINUX\r
- if(theAsioDriver)\r
- {\r
- delete theAsioDriver;\r
- theAsioDriver = 0;\r
- } \r
- info->driverVersion = 0;\r
- strcpy(info->name, "No ASIO Driver");\r
- theAsioDriver = getDriver();\r
- if(!theAsioDriver)\r
- {\r
- strcpy(info->errorMessage, "Not enough memory for the ASIO driver!"); \r
- return ASE_NotPresent;\r
- }\r
- if(!theAsioDriver->init(info->sysRef))\r
- {\r
- theAsioDriver->getErrorMessage(info->errorMessage);\r
- delete theAsioDriver;\r
- theAsioDriver = 0;\r
- return ASE_NotPresent;\r
- }\r
- strcpy(info->errorMessage, "No ASIO Driver Error");\r
- theAsioDriver->getDriverName(info->name);\r
- info->driverVersion = theAsioDriver->getDriverVersion();\r
- return ASE_OK;\r
-\r
-#else\r
-\r
- info->driverVersion = 0;\r
- strcpy(info->name, "No ASIO Driver");\r
- if(theAsioDriver) // must be loaded!\r
- {\r
- if(!theAsioDriver->init(info->sysRef))\r
- {\r
- theAsioDriver->getErrorMessage(info->errorMessage);\r
- theAsioDriver = 0;\r
- return ASE_NotPresent;\r
- } \r
-\r
- strcpy(info->errorMessage, "No ASIO Driver Error");\r
- theAsioDriver->getDriverName(info->name);\r
- info->driverVersion = theAsioDriver->getDriverVersion();\r
- return ASE_OK;\r
- }\r
- return ASE_NotPresent;\r
-\r
-#endif // !MAC\r
-}\r
-\r
-ASIOError ASIOExit(void)\r
-{\r
- if(theAsioDriver)\r
- {\r
-#if WINDOWS\r
- asioDrivers->removeCurrentDriver();\r
-#else\r
- delete theAsioDriver;\r
-#endif\r
- } \r
- theAsioDriver = 0;\r
- return ASE_OK;\r
-}\r
-\r
-ASIOError ASIOStart(void)\r
-{\r
- if(!theAsioDriver)\r
- return ASE_NotPresent;\r
- return theAsioDriver->start();\r
-}\r
-\r
-ASIOError ASIOStop(void)\r
-{\r
- if(!theAsioDriver)\r
- return ASE_NotPresent;\r
- return theAsioDriver->stop();\r
-}\r
-\r
-ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels)\r
-{\r
- if(!theAsioDriver)\r
- {\r
- *numInputChannels = *numOutputChannels = 0;\r
- return ASE_NotPresent;\r
- }\r
- return theAsioDriver->getChannels(numInputChannels, numOutputChannels);\r
-}\r
-\r
-ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency)\r
-{\r
- if(!theAsioDriver)\r
- {\r
- *inputLatency = *outputLatency = 0;\r
- return ASE_NotPresent;\r
- }\r
- return theAsioDriver->getLatencies(inputLatency, outputLatency);\r
-}\r
-\r
-ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity)\r
-{\r
- if(!theAsioDriver)\r
- {\r
- *minSize = *maxSize = *preferredSize = *granularity = 0;\r
- return ASE_NotPresent;\r
- }\r
- return theAsioDriver->getBufferSize(minSize, maxSize, preferredSize, granularity);\r
-}\r
-\r
-ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate)\r
-{\r
- if(!theAsioDriver)\r
- return ASE_NotPresent;\r
- return theAsioDriver->canSampleRate(sampleRate);\r
-}\r
-\r
-ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate)\r
-{\r
- if(!theAsioDriver)\r
- return ASE_NotPresent;\r
- return theAsioDriver->getSampleRate(currentRate);\r
-}\r
-\r
-ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate)\r
-{\r
- if(!theAsioDriver)\r
- return ASE_NotPresent;\r
- return theAsioDriver->setSampleRate(sampleRate);\r
-}\r
-\r
-ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources)\r
-{\r
- if(!theAsioDriver)\r
- {\r
- *numSources = 0;\r
- return ASE_NotPresent;\r
- }\r
- return theAsioDriver->getClockSources(clocks, numSources);\r
-}\r
-\r
-ASIOError ASIOSetClockSource(long reference)\r
-{\r
- if(!theAsioDriver)\r
- return ASE_NotPresent;\r
- return theAsioDriver->setClockSource(reference);\r
-}\r
-\r
-ASIOError ASIOGetSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp)\r
-{\r
- if(!theAsioDriver)\r
- return ASE_NotPresent;\r
- return theAsioDriver->getSamplePosition(sPos, tStamp);\r
-}\r
-\r
-ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info)\r
-{\r
- if(!theAsioDriver)\r
- {\r
- info->channelGroup = -1;\r
- info->type = ASIOSTInt16MSB;\r
- strcpy(info->name, "None");\r
- return ASE_NotPresent;\r
- }\r
- return theAsioDriver->getChannelInfo(info);\r
-}\r
-\r
-ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels,\r
- long bufferSize, ASIOCallbacks *callbacks)\r
-{\r
- if(!theAsioDriver)\r
- {\r
- ASIOBufferInfo *info = bufferInfos;\r
- for(long i = 0; i < numChannels; i++, info++)\r
- info->buffers[0] = info->buffers[1] = 0;\r
- return ASE_NotPresent;\r
- }\r
- return theAsioDriver->createBuffers(bufferInfos, numChannels, bufferSize, callbacks);\r
-}\r
-\r
-ASIOError ASIODisposeBuffers(void)\r
-{\r
- if(!theAsioDriver)\r
- return ASE_NotPresent;\r
- return theAsioDriver->disposeBuffers();\r
-}\r
-\r
-ASIOError ASIOControlPanel(void)\r
-{\r
- if(!theAsioDriver)\r
- return ASE_NotPresent;\r
- return theAsioDriver->controlPanel();\r
-}\r
-\r
-ASIOError ASIOFuture(long selector, void *opt)\r
-{\r
- if(!theAsioDriver)\r
- return ASE_NotPresent;\r
- return theAsioDriver->future(selector, opt);\r
-}\r
-\r
-ASIOError ASIOOutputReady(void)\r
-{\r
- if(!theAsioDriver)\r
- return ASE_NotPresent;\r
- return theAsioDriver->outputReady();\r
-}\r
-\r
-#if MAC\r
-} // extern "C"\r
-#pragma export off\r
-#endif\r
-\r
-\r
+++ /dev/null
-//---------------------------------------------------------------------------------------------------\r
-//---------------------------------------------------------------------------------------------------\r
-\r
-/*\r
- Steinberg Audio Stream I/O API\r
- (c) 1997 - 2013, Steinberg Media Technologies GmbH\r
-\r
- ASIO Interface Specification v 2.3\r
-\r
- 2005 - Added support for DSD sample data (in cooperation with Sony)\r
- 2012 - Added support for drop out detection\r
- \r
- \r
-\r
- basic concept is an i/o synchronous double-buffer scheme:\r
- \r
- on bufferSwitch(index == 0), host will read/write:\r
-\r
- after ASIOStart(), the\r
- read first input buffer A (index 0)\r
- | will be invalid (empty)\r
- * ------------------------\r
- |------------------------|-----------------------|\r
- | | |\r
- | Input Buffer A (0) | Input Buffer B (1) |\r
- | | |\r
- |------------------------|-----------------------|\r
- | | |\r
- | Output Buffer A (0) | Output Buffer B (1) |\r
- | | |\r
- |------------------------|-----------------------|\r
- * -------------------------\r
- | before calling ASIOStart(),\r
- write host will have filled output\r
- buffer B (index 1) already\r
-\r
- *please* take special care of proper statement of input\r
- and output latencies (see ASIOGetLatencies()), these\r
- control sequencer sync accuracy\r
-\r
-*/\r
-\r
-//---------------------------------------------------------------------------------------------------\r
-//---------------------------------------------------------------------------------------------------\r
-\r
-/*\r
-\r
-prototypes summary:\r
-\r
-ASIOError ASIOInit(ASIODriverInfo *info);\r
-ASIOError ASIOExit(void);\r
-ASIOError ASIOStart(void);\r
-ASIOError ASIOStop(void);\r
-ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels);\r
-ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency);\r
-ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity);\r
-ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate);\r
-ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate);\r
-ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate);\r
-ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources);\r
-ASIOError ASIOSetClockSource(long reference);\r
-ASIOError ASIOGetSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp);\r
-ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info);\r
-ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels,\r
- long bufferSize, ASIOCallbacks *callbacks);\r
-ASIOError ASIODisposeBuffers(void);\r
-ASIOError ASIOControlPanel(void);\r
-void *ASIOFuture(long selector, void *params);\r
-ASIOError ASIOOutputReady(void);\r
-\r
-*/\r
-\r
-//---------------------------------------------------------------------------------------------------\r
-//---------------------------------------------------------------------------------------------------\r
-\r
-#ifndef __ASIO_H\r
-#define __ASIO_H\r
-\r
-// force 4 byte alignment\r
-#if defined(_MSC_VER) && !defined(__MWERKS__) \r
-#pragma pack(push,4)\r
-#elif PRAGMA_ALIGN_SUPPORTED\r
-#pragma options align = native\r
-#endif\r
-\r
-//- - - - - - - - - - - - - - - - - - - - - - - - -\r
-// Type definitions\r
-//- - - - - - - - - - - - - - - - - - - - - - - - -\r
-\r
-// number of samples data type is 64 bit integer\r
-#if NATIVE_INT64\r
- typedef long long int ASIOSamples;\r
-#else\r
- typedef struct ASIOSamples {\r
- unsigned long hi;\r
- unsigned long lo;\r
- } ASIOSamples;\r
-#endif\r
-\r
-// Timestamp data type is 64 bit integer,\r
-// Time format is Nanoseconds.\r
-#if NATIVE_INT64\r
- typedef long long int ASIOTimeStamp ;\r
-#else\r
- typedef struct ASIOTimeStamp {\r
- unsigned long hi;\r
- unsigned long lo;\r
- } ASIOTimeStamp;\r
-#endif\r
-\r
-// Samplerates are expressed in IEEE 754 64 bit double float,\r
-// native format as host computer\r
-#if IEEE754_64FLOAT\r
- typedef double ASIOSampleRate;\r
-#else\r
- typedef struct ASIOSampleRate {\r
- char ieee[8];\r
- } ASIOSampleRate;\r
-#endif\r
-\r
-// Boolean values are expressed as long\r
-typedef long ASIOBool;\r
-enum {\r
- ASIOFalse = 0,\r
- ASIOTrue = 1\r
-};\r
-\r
-// Sample Types are expressed as long\r
-typedef long ASIOSampleType;\r
-enum {\r
- ASIOSTInt16MSB = 0,\r
- ASIOSTInt24MSB = 1, // used for 20 bits as well\r
- ASIOSTInt32MSB = 2,\r
- ASIOSTFloat32MSB = 3, // IEEE 754 32 bit float\r
- ASIOSTFloat64MSB = 4, // IEEE 754 64 bit double float\r
-\r
- // these are used for 32 bit data buffer, with different alignment of the data inside\r
- // 32 bit PCI bus systems can be more easily used with these\r
- ASIOSTInt32MSB16 = 8, // 32 bit data with 16 bit alignment\r
- ASIOSTInt32MSB18 = 9, // 32 bit data with 18 bit alignment\r
- ASIOSTInt32MSB20 = 10, // 32 bit data with 20 bit alignment\r
- ASIOSTInt32MSB24 = 11, // 32 bit data with 24 bit alignment\r
- \r
- ASIOSTInt16LSB = 16,\r
- ASIOSTInt24LSB = 17, // used for 20 bits as well\r
- ASIOSTInt32LSB = 18,\r
- ASIOSTFloat32LSB = 19, // IEEE 754 32 bit float, as found on Intel x86 architecture\r
- ASIOSTFloat64LSB = 20, // IEEE 754 64 bit double float, as found on Intel x86 architecture\r
-\r
- // these are used for 32 bit data buffer, with different alignment of the data inside\r
- // 32 bit PCI bus systems can more easily used with these\r
- ASIOSTInt32LSB16 = 24, // 32 bit data with 18 bit alignment\r
- ASIOSTInt32LSB18 = 25, // 32 bit data with 18 bit alignment\r
- ASIOSTInt32LSB20 = 26, // 32 bit data with 20 bit alignment\r
- ASIOSTInt32LSB24 = 27, // 32 bit data with 24 bit alignment\r
-\r
- // ASIO DSD format.\r
- ASIOSTDSDInt8LSB1 = 32, // DSD 1 bit data, 8 samples per byte. First sample in Least significant bit.\r
- ASIOSTDSDInt8MSB1 = 33, // DSD 1 bit data, 8 samples per byte. First sample in Most significant bit.\r
- ASIOSTDSDInt8NER8 = 40, // DSD 8 bit data, 1 sample per byte. No Endianness required.\r
-\r
- ASIOSTLastEntry\r
-};\r
-\r
-/*-----------------------------------------------------------------------------\r
-// DSD operation and buffer layout\r
-// Definition by Steinberg/Sony Oxford.\r
-//\r
-// We have tried to treat DSD as PCM and so keep a consistant structure across\r
-// the ASIO interface.\r
-//\r
-// DSD's sample rate is normally referenced as a multiple of 44.1Khz, so\r
-// the standard sample rate is refered to as 64Fs (or 2.8224Mhz). We looked\r
-// at making a special case for DSD and adding a field to the ASIOFuture that\r
-// would allow the user to select the Over Sampleing Rate (OSR) as a seperate\r
-// entity but decided in the end just to treat it as a simple value of\r
-// 2.8224Mhz and use the standard interface to set it.\r
-//\r
-// The second problem was the "word" size, in PCM the word size is always a\r
-// greater than or equal to 8 bits (a byte). This makes life easy as we can\r
-// then pack the samples into the "natural" size for the machine.\r
-// In DSD the "word" size is 1 bit. This is not a major problem and can easily\r
-// be dealt with if we ensure that we always deal with a multiple of 8 samples.\r
-//\r
-// DSD brings with it another twist to the Endianness religion. How are the\r
-// samples packed into the byte. It would be nice to just say the most significant\r
-// bit is always the first sample, however there would then be a performance hit\r
-// on little endian machines. Looking at how some of the processing goes...\r
-// Little endian machines like the first sample to be in the Least Significant Bit,\r
-// this is because when you write it to memory the data is in the correct format\r
-// to be shifted in and out of the words.\r
-// Big endian machine prefer the first sample to be in the Most Significant Bit,\r
-// again for the same reasion.\r
-//\r
-// And just when things were looking really muddy there is a proposed extension to\r
-// DSD that uses 8 bit word sizes. It does not care what endianness you use.\r
-//\r
-// Switching the driver between DSD and PCM mode\r
-// ASIOFuture allows for extending the ASIO API quite transparently.\r
-// See kAsioSetIoFormat, kAsioGetIoFormat, kAsioCanDoIoFormat\r
-//\r
-//-----------------------------------------------------------------------------*/\r
-\r
-\r
-//- - - - - - - - - - - - - - - - - - - - - - - - -\r
-// Error codes\r
-//- - - - - - - - - - - - - - - - - - - - - - - - -\r
-\r
-typedef long ASIOError;\r
-enum {\r
- ASE_OK = 0, // This value will be returned whenever the call succeeded\r
- ASE_SUCCESS = 0x3f4847a0, // unique success return value for ASIOFuture calls\r
- ASE_NotPresent = -1000, // hardware input or output is not present or available\r
- ASE_HWMalfunction, // hardware is malfunctioning (can be returned by any ASIO function)\r
- ASE_InvalidParameter, // input parameter invalid\r
- ASE_InvalidMode, // hardware is in a bad mode or used in a bad mode\r
- ASE_SPNotAdvancing, // hardware is not running when sample position is inquired\r
- ASE_NoClock, // sample clock or rate cannot be determined or is not present\r
- ASE_NoMemory // not enough memory for completing the request\r
-};\r
-\r
-//---------------------------------------------------------------------------------------------------\r
-//---------------------------------------------------------------------------------------------------\r
-\r
-//- - - - - - - - - - - - - - - - - - - - - - - - -\r
-// Time Info support\r
-//- - - - - - - - - - - - - - - - - - - - - - - - -\r
-\r
-typedef struct ASIOTimeCode\r
-{ \r
- double speed; // speed relation (fraction of nominal speed)\r
- // optional; set to 0. or 1. if not supported\r
- ASIOSamples timeCodeSamples; // time in samples\r
- unsigned long flags; // some information flags (see below)\r
- char future[64];\r
-} ASIOTimeCode;\r
-\r
-typedef enum ASIOTimeCodeFlags\r
-{\r
- kTcValid = 1,\r
- kTcRunning = 1 << 1,\r
- kTcReverse = 1 << 2,\r
- kTcOnspeed = 1 << 3,\r
- kTcStill = 1 << 4,\r
- \r
- kTcSpeedValid = 1 << 8\r
-} ASIOTimeCodeFlags;\r
-\r
-typedef struct AsioTimeInfo\r
-{\r
- double speed; // absolute speed (1. = nominal)\r
- ASIOTimeStamp systemTime; // system time related to samplePosition, in nanoseconds\r
- // on mac, must be derived from Microseconds() (not UpTime()!)\r
- // on windows, must be derived from timeGetTime()\r
- ASIOSamples samplePosition;\r
- ASIOSampleRate sampleRate; // current rate\r
- unsigned long flags; // (see below)\r
- char reserved[12];\r
-} AsioTimeInfo;\r
-\r
-typedef enum AsioTimeInfoFlags\r
-{\r
- kSystemTimeValid = 1, // must always be valid\r
- kSamplePositionValid = 1 << 1, // must always be valid\r
- kSampleRateValid = 1 << 2,\r
- kSpeedValid = 1 << 3,\r
- \r
- kSampleRateChanged = 1 << 4,\r
- kClockSourceChanged = 1 << 5\r
-} AsioTimeInfoFlags;\r
-\r
-typedef struct ASIOTime // both input/output\r
-{\r
- long reserved[4]; // must be 0\r
- struct AsioTimeInfo timeInfo; // required\r
- struct ASIOTimeCode timeCode; // optional, evaluated if (timeCode.flags & kTcValid)\r
-} ASIOTime;\r
-\r
-/*\r
-\r
-using time info:\r
-it is recommended to use the new method with time info even if the asio\r
-device does not support timecode; continuous calls to ASIOGetSamplePosition\r
-and ASIOGetSampleRate are avoided, and there is a more defined relationship\r
-between callback time and the time info.\r
-\r
-see the example below.\r
-to initiate time info mode, after you have received the callbacks pointer in\r
-ASIOCreateBuffers, you will call the asioMessage callback with kAsioSupportsTimeInfo\r
-as the argument. if this returns 1, host has accepted time info mode.\r
-now host expects the new callback bufferSwitchTimeInfo to be used instead\r
-of the old bufferSwitch method. the ASIOTime structure is assumed to be valid\r
-and accessible until the callback returns.\r
-\r
-using time code:\r
-if the device supports reading time code, it will call host's asioMessage callback\r
-with kAsioSupportsTimeCode as the selector. it may then fill the according\r
-fields and set the kTcValid flag.\r
-host will call the future method with the kAsioEnableTimeCodeRead selector when\r
-it wants to enable or disable tc reading by the device. you should also support\r
-the kAsioCanTimeInfo and kAsioCanTimeCode selectors in ASIOFuture (see example).\r
-\r
-note:\r
-the AsioTimeInfo/ASIOTimeCode pair is supposed to work in both directions.\r
-as a matter of convention, the relationship between the sample\r
-position counter and the time code at buffer switch time is\r
-(ignoring offset between tc and sample pos when tc is running):\r
-\r
-on input: sample 0 -> input buffer sample 0 -> time code 0\r
-on output: sample 0 -> output buffer sample 0 -> time code 0\r
-\r
-this means that for 'real' calculations, one has to take into account\r
-the according latencies.\r
-\r
-example:\r
-\r
-ASIOTime asioTime;\r
-\r
-in createBuffers()\r
-{\r
- memset(&asioTime, 0, sizeof(ASIOTime));\r
- AsioTimeInfo* ti = &asioTime.timeInfo;\r
- ti->sampleRate = theSampleRate;\r
- ASIOTimeCode* tc = &asioTime.timeCode;\r
- tc->speed = 1.;\r
- timeInfoMode = false;\r
- canTimeCode = false;\r
- if(callbacks->asioMessage(kAsioSupportsTimeInfo, 0, 0, 0) == 1)\r
- {\r
- timeInfoMode = true;\r
-#if kCanTimeCode\r
- if(callbacks->asioMessage(kAsioSupportsTimeCode, 0, 0, 0) == 1)\r
- canTimeCode = true;\r
-#endif\r
- }\r
-}\r
-\r
-void switchBuffers(long doubleBufferIndex, bool processNow)\r
-{\r
- if(timeInfoMode)\r
- {\r
- AsioTimeInfo* ti = &asioTime.timeInfo;\r
- ti->flags = kSystemTimeValid | kSamplePositionValid | kSampleRateValid;\r
- ti->systemTime = theNanoSeconds;\r
- ti->samplePosition = theSamplePosition;\r
- if(ti->sampleRate != theSampleRate)\r
- ti->flags |= kSampleRateChanged;\r
- ti->sampleRate = theSampleRate;\r
-\r
-#if kCanTimeCode\r
- if(canTimeCode && timeCodeEnabled)\r
- {\r
- ASIOTimeCode* tc = &asioTime.timeCode;\r
- tc->timeCodeSamples = tcSamples; // tc in samples\r
- tc->flags = kTcValid | kTcRunning | kTcOnspeed; // if so...\r
- }\r
- ASIOTime* bb = callbacks->bufferSwitchTimeInfo(&asioTime, doubleBufferIndex, processNow ? ASIOTrue : ASIOFalse);\r
-#else\r
- callbacks->bufferSwitchTimeInfo(&asioTime, doubleBufferIndex, processNow ? ASIOTrue : ASIOFalse);\r
-#endif\r
- }\r
- else\r
- callbacks->bufferSwitch(doubleBufferIndex, ASIOFalse);\r
-}\r
-\r
-ASIOError ASIOFuture(long selector, void *params)\r
-{\r
- switch(selector)\r
- {\r
- case kAsioEnableTimeCodeRead:\r
- timeCodeEnabled = true;\r
- return ASE_SUCCESS;\r
- case kAsioDisableTimeCodeRead:\r
- timeCodeEnabled = false;\r
- return ASE_SUCCESS;\r
- case kAsioCanTimeInfo:\r
- return ASE_SUCCESS;\r
- #if kCanTimeCode\r
- case kAsioCanTimeCode:\r
- return ASE_SUCCESS;\r
- #endif\r
- }\r
- return ASE_NotPresent;\r
-};\r
-\r
-*/\r
-\r
-//- - - - - - - - - - - - - - - - - - - - - - - - -\r
-// application's audio stream handler callbacks\r
-//- - - - - - - - - - - - - - - - - - - - - - - - -\r
-\r
-typedef struct ASIOCallbacks\r
-{\r
- void (*bufferSwitch) (long doubleBufferIndex, ASIOBool directProcess);\r
- // bufferSwitch indicates that both input and output are to be processed.\r
- // the current buffer half index (0 for A, 1 for B) determines\r
- // - the output buffer that the host should start to fill. the other buffer\r
- // will be passed to output hardware regardless of whether it got filled\r
- // in time or not.\r
- // - the input buffer that is now filled with incoming data. Note that\r
- // because of the synchronicity of i/o, the input always has at\r
- // least one buffer latency in relation to the output.\r
- // directProcess suggests to the host whether it should immedeately\r
- // start processing (directProcess == ASIOTrue), or whether its process\r
- // should be deferred because the call comes from a very low level\r
- // (for instance, a high level priority interrupt), and direct processing\r
- // would cause timing instabilities for the rest of the system. If in doubt,\r
- // directProcess should be set to ASIOFalse.\r
- // Note: bufferSwitch may be called at interrupt time for highest efficiency.\r
-\r
- void (*sampleRateDidChange) (ASIOSampleRate sRate);\r
- // gets called when the AudioStreamIO detects a sample rate change\r
- // If sample rate is unknown, 0 is passed (for instance, clock loss\r
- // when externally synchronized).\r
-\r
- long (*asioMessage) (long selector, long value, void* message, double* opt);\r
- // generic callback for various purposes, see selectors below.\r
- // note this is only present if the asio version is 2 or higher\r
-\r
- ASIOTime* (*bufferSwitchTimeInfo) (ASIOTime* params, long doubleBufferIndex, ASIOBool directProcess);\r
- // new callback with time info. makes ASIOGetSamplePosition() and various\r
- // calls to ASIOGetSampleRate obsolete,\r
- // and allows for timecode sync etc. to be preferred; will be used if\r
- // the driver calls asioMessage with selector kAsioSupportsTimeInfo.\r
-} ASIOCallbacks;\r
-\r
-// asioMessage selectors\r
-enum\r
-{\r
- kAsioSelectorSupported = 1, // selector in <value>, returns 1L if supported,\r
- // 0 otherwise\r
- kAsioEngineVersion, // returns engine (host) asio implementation version,\r
- // 2 or higher\r
- kAsioResetRequest, // request driver reset. if accepted, this\r
- // will close the driver (ASIO_Exit() ) and\r
- // re-open it again (ASIO_Init() etc). some\r
- // drivers need to reconfigure for instance\r
- // when the sample rate changes, or some basic\r
- // changes have been made in ASIO_ControlPanel().\r
- // returns 1L; note the request is merely passed\r
- // to the application, there is no way to determine\r
- // if it gets accepted at this time (but it usually\r
- // will be).\r
- kAsioBufferSizeChange, // not yet supported, will currently always return 0L.\r
- // for now, use kAsioResetRequest instead.\r
- // once implemented, the new buffer size is expected\r
- // in <value>, and on success returns 1L\r
- kAsioResyncRequest, // the driver went out of sync, such that\r
- // the timestamp is no longer valid. this\r
- // is a request to re-start the engine and\r
- // slave devices (sequencer). returns 1 for ok,\r
- // 0 if not supported.\r
- kAsioLatenciesChanged, // the drivers latencies have changed. The engine\r
- // will refetch the latencies.\r
- kAsioSupportsTimeInfo, // if host returns true here, it will expect the\r
- // callback bufferSwitchTimeInfo to be called instead\r
- // of bufferSwitch\r
- kAsioSupportsTimeCode, // \r
- kAsioMMCCommand, // unused - value: number of commands, message points to mmc commands\r
- kAsioSupportsInputMonitor, // kAsioSupportsXXX return 1 if host supports this\r
- kAsioSupportsInputGain, // unused and undefined\r
- kAsioSupportsInputMeter, // unused and undefined\r
- kAsioSupportsOutputGain, // unused and undefined\r
- kAsioSupportsOutputMeter, // unused and undefined\r
- kAsioOverload, // driver detected an overload\r
-\r
- kAsioNumMessageSelectors\r
-};\r
-\r
-//---------------------------------------------------------------------------------------------------\r
-//---------------------------------------------------------------------------------------------------\r
-\r
-//- - - - - - - - - - - - - - - - - - - - - - - - -\r
-// (De-)Construction\r
-//- - - - - - - - - - - - - - - - - - - - - - - - -\r
-\r
-typedef struct ASIODriverInfo\r
-{\r
- long asioVersion; // currently, 2\r
- long driverVersion; // driver specific\r
- char name[32];\r
- char errorMessage[124];\r
- void *sysRef; // on input: system reference\r
- // (Windows: application main window handle, Mac & SGI: 0)\r
-} ASIODriverInfo;\r
-\r
-ASIOError ASIOInit(ASIODriverInfo *info);\r
-/* Purpose:\r
- Initialize the AudioStreamIO.\r
- Parameter:\r
- info: pointer to an ASIODriver structure:\r
- - asioVersion:\r
- - on input, the host version. *** Note *** this is 0 for earlier asio\r
- implementations, and the asioMessage callback is implemeted\r
- only if asioVersion is 2 or greater. sorry but due to a design fault\r
- the driver doesn't have access to the host version in ASIOInit :-(\r
- added selector for host (engine) version in the asioMessage callback\r
- so we're ok from now on.\r
- - on return, asio implementation version.\r
- older versions are 1\r
- if you support this version (namely, ASIO_outputReady() )\r
- this should be 2 or higher. also see the note in\r
- ASIO_getTimeStamp() !\r
- - version: on return, the driver version (format is driver specific)\r
- - name: on return, a null-terminated string containing the driver's name\r
- - error message: on return, should contain a user message describing\r
- the type of error that occured during ASIOInit(), if any.\r
- - sysRef: platform specific\r
- Returns:\r
- If neither input nor output is present ASE_NotPresent\r
- will be returned.\r
- ASE_NoMemory, ASE_HWMalfunction are other possible error conditions\r
-*/\r
-\r
-ASIOError ASIOExit(void);\r
-/* Purpose:\r
- Terminates the AudioStreamIO.\r
- Parameter:\r
- None.\r
- Returns:\r
- If neither input nor output is present ASE_NotPresent\r
- will be returned.\r
- Notes: this implies ASIOStop() and ASIODisposeBuffers(),\r
- meaning that no host callbacks must be accessed after ASIOExit().\r
-*/\r
-\r
-//- - - - - - - - - - - - - - - - - - - - - - - - -\r
-// Start/Stop\r
-//- - - - - - - - - - - - - - - - - - - - - - - - -\r
-\r
-ASIOError ASIOStart(void);\r
-/* Purpose:\r
- Start input and output processing synchronously.\r
- This will\r
- - reset the sample counter to zero\r
- - start the hardware (both input and output)\r
- The first call to the hosts' bufferSwitch(index == 0) then tells\r
- the host to read from input buffer A (index 0), and start\r
- processing to output buffer A while output buffer B (which\r
- has been filled by the host prior to calling ASIOStart())\r
- is possibly sounding (see also ASIOGetLatencies()) \r
- Parameter:\r
- None.\r
- Returns:\r
- If neither input nor output is present, ASE_NotPresent\r
- will be returned.\r
- If the hardware fails to start, ASE_HWMalfunction will be returned.\r
- Notes:\r
- There is no restriction on the time that ASIOStart() takes\r
- to perform (that is, it is not considered a realtime trigger).\r
-*/\r
-\r
-ASIOError ASIOStop(void);\r
-/* Purpose:\r
- Stops input and output processing altogether.\r
- Parameter:\r
- None.\r
- Returns:\r
- If neither input nor output is present ASE_NotPresent\r
- will be returned.\r
- Notes:\r
- On return from ASIOStop(), the driver must in no\r
- case call the hosts' bufferSwitch() routine.\r
-*/\r
-\r
-//- - - - - - - - - - - - - - - - - - - - - - - - -\r
-// Inquiry methods and sample rate\r
-//- - - - - - - - - - - - - - - - - - - - - - - - -\r
-\r
-ASIOError ASIOGetChannels(long *numInputChannels, long *numOutputChannels);\r
-/* Purpose:\r
- Returns number of individual input/output channels.\r
- Parameter:\r
- numInputChannels will hold the number of available input channels\r
- numOutputChannels will hold the number of available output channels\r
- Returns:\r
- If no input/output is present ASE_NotPresent will be returned.\r
- If only inputs, or only outputs are available, the according\r
- other parameter will be zero, and ASE_OK is returned.\r
-*/\r
-\r
-ASIOError ASIOGetLatencies(long *inputLatency, long *outputLatency);\r
-/* Purpose:\r
- Returns the input and output latencies. This includes\r
- device specific delays, like FIFOs etc.\r
- Parameter:\r
- inputLatency will hold the 'age' of the first sample frame\r
- in the input buffer when the hosts reads it in bufferSwitch()\r
- (this is theoretical, meaning it does not include the overhead\r
- and delay between the actual physical switch, and the time\r
- when bufferSitch() enters).\r
- This will usually be the size of one block in sample frames, plus\r
- device specific latencies.\r
-\r
- outputLatency will specify the time between the buffer switch,\r
- and the time when the next play buffer will start to sound.\r
- The next play buffer is defined as the one the host starts\r
- processing after (or at) bufferSwitch(), indicated by the\r
- index parameter (0 for buffer A, 1 for buffer B).\r
- It will usually be either one block, if the host writes directly\r
- to a dma buffer, or two or more blocks if the buffer is 'latched' by\r
- the driver. As an example, on ASIOStart(), the host will have filled\r
- the play buffer at index 1 already; when it gets the callback (with\r
- the parameter index == 0), this tells it to read from the input\r
- buffer 0, and start to fill the play buffer 0 (assuming that now\r
- play buffer 1 is already sounding). In this case, the output\r
- latency is one block. If the driver decides to copy buffer 1\r
- at that time, and pass it to the hardware at the next slot (which\r
- is most commonly done, but should be avoided), the output latency\r
- becomes two blocks instead, resulting in a total i/o latency of at least\r
- 3 blocks. As memory access is the main bottleneck in native dsp processing,\r
- and to acheive less latency, it is highly recommended to try to avoid\r
- copying (this is also why the driver is the owner of the buffers). To\r
- summarize, the minimum i/o latency can be acheived if the input buffer\r
- is processed by the host into the output buffer which will physically\r
- start to sound on the next time slice. Also note that the host expects\r
- the bufferSwitch() callback to be accessed for each time slice in order\r
- to retain sync, possibly recursively; if it fails to process a block in\r
- time, it will suspend its operation for some time in order to recover.\r
- Returns:\r
- If no input/output is present ASE_NotPresent will be returned.\r
-*/\r
-\r
-ASIOError ASIOGetBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity);\r
-/* Purpose:\r
- Returns min, max, and preferred buffer sizes for input/output\r
- Parameter:\r
- minSize will hold the minimum buffer size\r
- maxSize will hold the maxium possible buffer size\r
- preferredSize will hold the preferred buffer size (a size which\r
- best fits performance and hardware requirements)\r
- granularity will hold the granularity at which buffer sizes\r
- may differ. Usually, the buffer size will be a power of 2;\r
- in this case, granularity will hold -1 on return, signalling\r
- possible buffer sizes starting from minSize, increased in\r
- powers of 2 up to maxSize.\r
- Returns:\r
- If no input/output is present ASE_NotPresent will be returned.\r
- Notes:\r
- When minimum and maximum buffer size are equal,\r
- the preferred buffer size has to be the same value as well; granularity\r
- should be 0 in this case.\r
-*/\r
-\r
-ASIOError ASIOCanSampleRate(ASIOSampleRate sampleRate);\r
-/* Purpose:\r
- Inquires the hardware for the available sample rates.\r
- Parameter:\r
- sampleRate is the rate in question.\r
- Returns:\r
- If the inquired sample rate is not supported, ASE_NoClock will be returned.\r
- If no input/output is present ASE_NotPresent will be returned.\r
-*/\r
-ASIOError ASIOGetSampleRate(ASIOSampleRate *currentRate);\r
-/* Purpose:\r
- Get the current sample Rate.\r
- Parameter:\r
- currentRate will hold the current sample rate on return.\r
- Returns:\r
- If sample rate is unknown, sampleRate will be 0 and ASE_NoClock will be returned.\r
- If no input/output is present ASE_NotPresent will be returned.\r
- Notes:\r
-*/\r
-\r
-ASIOError ASIOSetSampleRate(ASIOSampleRate sampleRate);\r
-/* Purpose:\r
- Set the hardware to the requested sample Rate. If sampleRate == 0,\r
- enable external sync.\r
- Parameter:\r
- sampleRate: on input, the requested rate\r
- Returns:\r
- If sampleRate is unknown ASE_NoClock will be returned.\r
- If the current clock is external, and sampleRate is != 0,\r
- ASE_InvalidMode will be returned\r
- If no input/output is present ASE_NotPresent will be returned.\r
- Notes:\r
-*/\r
-\r
-typedef struct ASIOClockSource\r
-{\r
- long index; // as used for ASIOSetClockSource()\r
- long associatedChannel; // for instance, S/PDIF or AES/EBU\r
- long associatedGroup; // see channel groups (ASIOGetChannelInfo())\r
- ASIOBool isCurrentSource; // ASIOTrue if this is the current clock source\r
- char name[32]; // for user selection\r
-} ASIOClockSource;\r
-\r
-ASIOError ASIOGetClockSources(ASIOClockSource *clocks, long *numSources);\r
-/* Purpose:\r
- Get the available external audio clock sources\r
- Parameter:\r
- clocks points to an array of ASIOClockSource structures:\r
- - index: this is used to identify the clock source\r
- when ASIOSetClockSource() is accessed, should be\r
- an index counting from zero\r
- - associatedInputChannel: the first channel of an associated\r
- input group, if any.\r
- - associatedGroup: the group index of that channel.\r
- groups of channels are defined to seperate for\r
- instance analog, S/PDIF, AES/EBU, ADAT connectors etc,\r
- when present simultaniously. Note that associated channel\r
- is enumerated according to numInputs/numOutputs, means it\r
- is independant from a group (see also ASIOGetChannelInfo())\r
- inputs are associated to a clock if the physical connection\r
- transfers both data and clock (like S/PDIF, AES/EBU, or\r
- ADAT inputs). if there is no input channel associated with\r
- the clock source (like Word Clock, or internal oscillator), both\r
- associatedChannel and associatedGroup should be set to -1.\r
- - isCurrentSource: on exit, ASIOTrue if this is the current clock\r
- source, ASIOFalse else\r
- - name: a null-terminated string for user selection of the available sources.\r
- numSources:\r
- on input: the number of allocated array members\r
- on output: the number of available clock sources, at least\r
- 1 (internal clock generator).\r
- Returns:\r
- If no input/output is present ASE_NotPresent will be returned.\r
- Notes:\r
-*/\r
-\r
-ASIOError ASIOSetClockSource(long index);\r
-/* Purpose:\r
- Set the audio clock source\r
- Parameter:\r
- index as obtained from an inquiry to ASIOGetClockSources()\r
- Returns:\r
- If no input/output is present ASE_NotPresent will be returned.\r
- If the clock can not be selected because an input channel which\r
- carries the current clock source is active, ASE_InvalidMode\r
- *may* be returned (this depends on the properties of the driver\r
- and/or hardware).\r
- Notes:\r
- Should *not* return ASE_NoClock if there is no clock signal present\r
- at the selected source; this will be inquired via ASIOGetSampleRate().\r
- It should call the host callback procedure sampleRateHasChanged(),\r
- if the switch causes a sample rate change, or if no external clock\r
- is present at the selected source.\r
-*/\r
-\r
-ASIOError ASIOGetSamplePosition (ASIOSamples *sPos, ASIOTimeStamp *tStamp);\r
-/* Purpose:\r
- Inquires the sample position/time stamp pair.\r
- Parameter:\r
- sPos will hold the sample position on return. The sample\r
- position is reset to zero when ASIOStart() gets called.\r
- tStamp will hold the system time when the sample position\r
- was latched.\r
- Returns:\r
- If no input/output is present, ASE_NotPresent will be returned.\r
- If there is no clock, ASE_SPNotAdvancing will be returned.\r
- Notes:\r
-\r
- in order to be able to synchronise properly,\r
- the sample position / time stamp pair must refer to the current block,\r
- that is, the engine will call ASIOGetSamplePosition() in its bufferSwitch()\r
- callback and expect the time for the current block. thus, when requested\r
- in the very first bufferSwitch after ASIO_Start(), the sample position\r
- should be zero, and the time stamp should refer to the very time where\r
- the stream was started. it also means that the sample position must be\r
- block aligned. the driver must ensure proper interpolation if the system\r
- time can not be determined for the block position. the driver is responsible\r
- for precise time stamps as it usually has most direct access to lower\r
- level resources. proper behaviour of ASIO_GetSamplePosition() and ASIO_GetLatencies()\r
- are essential for precise media synchronization!\r
-*/\r
-\r
-typedef struct ASIOChannelInfo\r
-{\r
- long channel; // on input, channel index\r
- ASIOBool isInput; // on input\r
- ASIOBool isActive; // on exit\r
- long channelGroup; // dto\r
- ASIOSampleType type; // dto\r
- char name[32]; // dto\r
-} ASIOChannelInfo;\r
-\r
-ASIOError ASIOGetChannelInfo(ASIOChannelInfo *info);\r
-/* Purpose:\r
- retreive information about the nature of a channel\r
- Parameter:\r
- info: pointer to a ASIOChannelInfo structure with\r
- - channel: on input, the channel index of the channel in question.\r
- - isInput: on input, ASIOTrue if info for an input channel is\r
- requested, else output\r
- - channelGroup: on return, the channel group that the channel\r
- belongs to. For drivers which support different types of\r
- channels, like analog, S/PDIF, AES/EBU, ADAT etc interfaces,\r
- there should be a reasonable grouping of these types. Groups\r
- are always independant form a channel index, that is, a channel\r
- index always counts from 0 to numInputs/numOutputs regardless\r
- of the group it may belong to.\r
- There will always be at least one group (group 0). Please\r
- also note that by default, the host may decide to activate\r
- channels 0 and 1; thus, these should belong to the most\r
- useful type (analog i/o, if present).\r
- - type: on return, contains the sample type of the channel\r
- - isActive: on return, ASIOTrue if channel is active as it was\r
- installed by ASIOCreateBuffers(), ASIOFalse else\r
- - name: describing the type of channel in question. Used to allow\r
- for user selection, and enabling of specific channels. examples:\r
- "Analog In", "SPDIF Out" etc\r
- Returns:\r
- If no input/output is present ASE_NotPresent will be returned.\r
- Notes:\r
- If possible, the string should be organised such that the first\r
- characters are most significantly describing the nature of the\r
- port, to allow for identification even if the view showing the\r
- port name is too small to display more than 8 characters, for\r
- instance.\r
-*/\r
-\r
-//- - - - - - - - - - - - - - - - - - - - - - - - -\r
-// Buffer preparation\r
-//- - - - - - - - - - - - - - - - - - - - - - - - -\r
-\r
-typedef struct ASIOBufferInfo\r
-{\r
- ASIOBool isInput; // on input: ASIOTrue: input, else output\r
- long channelNum; // on input: channel index\r
- void *buffers[2]; // on output: double buffer addresses\r
-} ASIOBufferInfo;\r
-\r
-ASIOError ASIOCreateBuffers(ASIOBufferInfo *bufferInfos, long numChannels,\r
- long bufferSize, ASIOCallbacks *callbacks);\r
-\r
-/* Purpose:\r
- Allocates input/output buffers for all input and output channels to be activated.\r
- Parameter:\r
- bufferInfos is a pointer to an array of ASIOBufferInfo structures:\r
- - isInput: on input, ASIOTrue if the buffer is to be allocated\r
- for an input, output buffer else\r
- - channelNum: on input, the index of the channel in question\r
- (counting from 0)\r
- - buffers: on exit, 2 pointers to the halves of the channels' double-buffer.\r
- the size of the buffer(s) of course depend on both the ASIOSampleType\r
- as obtained from ASIOGetChannelInfo(), and bufferSize\r
- numChannels is the sum of all input and output channels to be created;\r
- thus bufferInfos is a pointer to an array of numChannels ASIOBufferInfo\r
- structures.\r
- bufferSize selects one of the possible buffer sizes as obtained from\r
- ASIOGetBufferSizes().\r
- callbacks is a pointer to an ASIOCallbacks structure.\r
- Returns:\r
- If not enough memory is available ASE_NoMemory will be returned.\r
- If no input/output is present ASE_NotPresent will be returned.\r
- If bufferSize is not supported, or one or more of the bufferInfos elements\r
- contain invalid settings, ASE_InvalidMode will be returned.\r
- Notes:\r
- If individual channel selection is not possible but requested,\r
- the driver has to handle this. namely, bufferSwitch() will only\r
- have filled buffers of enabled outputs. If possible, processing\r
- and buss activities overhead should be avoided for channels which\r
- were not enabled here.\r
-*/\r
-\r
-ASIOError ASIODisposeBuffers(void);\r
-/* Purpose:\r
- Releases all buffers for the device.\r
- Parameter:\r
- None.\r
- Returns:\r
- If no buffer were ever prepared, ASE_InvalidMode will be returned.\r
- If no input/output is present ASE_NotPresent will be returned.\r
- Notes:\r
- This implies ASIOStop().\r
-*/\r
-\r
-ASIOError ASIOControlPanel(void);\r
-/* Purpose:\r
- request the driver to start a control panel component\r
- for device specific user settings. This will not be\r
- accessed on some platforms (where the component is accessed\r
- instead).\r
- Parameter:\r
- None.\r
- Returns:\r
- If no panel is available ASE_NotPresent will be returned.\r
- Actually, the return code is ignored.\r
- Notes:\r
- if the user applied settings which require a re-configuration\r
- of parts or all of the enigine and/or driver (such as a change of\r
- the block size), the asioMessage callback can be used (see\r
- ASIO_Callbacks).\r
-*/\r
-\r
-ASIOError ASIOFuture(long selector, void *params);\r
-/* Purpose:\r
- various\r
- Parameter:\r
- selector: operation Code as to be defined. zero is reserved for\r
- testing purposes.\r
- params: depends on the selector; usually pointer to a structure\r
- for passing and retreiving any type and amount of parameters.\r
- Returns:\r
- the return value is also selector dependant. if the selector\r
- is unknown, ASE_InvalidParameter should be returned to prevent\r
- further calls with this selector. on success, ASE_SUCCESS\r
- must be returned (note: ASE_OK is *not* sufficient!)\r
- Notes:\r
- see selectors defined below. \r
-*/\r
-\r
-enum\r
-{\r
- kAsioEnableTimeCodeRead = 1, // no arguments\r
- kAsioDisableTimeCodeRead, // no arguments\r
- kAsioSetInputMonitor, // ASIOInputMonitor* in params\r
- kAsioTransport, // ASIOTransportParameters* in params\r
- kAsioSetInputGain, // ASIOChannelControls* in params, apply gain\r
- kAsioGetInputMeter, // ASIOChannelControls* in params, fill meter\r
- kAsioSetOutputGain, // ASIOChannelControls* in params, apply gain\r
- kAsioGetOutputMeter, // ASIOChannelControls* in params, fill meter\r
- kAsioCanInputMonitor, // no arguments for kAsioCanXXX selectors\r
- kAsioCanTimeInfo,\r
- kAsioCanTimeCode,\r
- kAsioCanTransport,\r
- kAsioCanInputGain,\r
- kAsioCanInputMeter,\r
- kAsioCanOutputGain,\r
- kAsioCanOutputMeter,\r
- kAsioOptionalOne,\r
- \r
- // DSD support\r
- // The following extensions are required to allow switching\r
- // and control of the DSD subsystem.\r
- kAsioSetIoFormat = 0x23111961, /* ASIOIoFormat * in params. */\r
- kAsioGetIoFormat = 0x23111983, /* ASIOIoFormat * in params. */\r
- kAsioCanDoIoFormat = 0x23112004, /* ASIOIoFormat * in params. */\r
- \r
- // Extension for drop out detection\r
- kAsioCanReportOverload = 0x24042012, /* return ASE_SUCCESS if driver can detect and report overloads */\r
- \r
- kAsioGetInternalBufferSamples = 0x25042012 /* ASIOInternalBufferInfo * in params. Deliver size of driver internal buffering, return ASE_SUCCESS if supported */\r
-};\r
-\r
-typedef struct ASIOInputMonitor\r
-{\r
- long input; // this input was set to monitor (or off), -1: all\r
- long output; // suggested output for monitoring the input (if so)\r
- long gain; // suggested gain, ranging 0 - 0x7fffffffL (-inf to +12 dB)\r
- ASIOBool state; // ASIOTrue => on, ASIOFalse => off\r
- long pan; // suggested pan, 0 => all left, 0x7fffffff => right\r
-} ASIOInputMonitor;\r
-\r
-typedef struct ASIOChannelControls\r
-{\r
- long channel; // on input, channel index\r
- ASIOBool isInput; // on input\r
- long gain; // on input, ranges 0 thru 0x7fffffff\r
- long meter; // on return, ranges 0 thru 0x7fffffff\r
- char future[32];\r
-} ASIOChannelControls;\r
-\r
-typedef struct ASIOTransportParameters\r
-{\r
- long command; // see enum below\r
- ASIOSamples samplePosition;\r
- long track;\r
- long trackSwitches[16]; // 512 tracks on/off\r
- char future[64];\r
-} ASIOTransportParameters;\r
-\r
-enum\r
-{\r
- kTransStart = 1,\r
- kTransStop,\r
- kTransLocate, // to samplePosition\r
- kTransPunchIn,\r
- kTransPunchOut,\r
- kTransArmOn, // track\r
- kTransArmOff, // track\r
- kTransMonitorOn, // track\r
- kTransMonitorOff, // track\r
- kTransArm, // trackSwitches\r
- kTransMonitor // trackSwitches\r
-};\r
-\r
-/*\r
-// DSD support\r
-// Some notes on how to use ASIOIoFormatType.\r
-//\r
-// The caller will fill the format with the request types.\r
-// If the board can do the request then it will leave the\r
-// values unchanged. If the board does not support the\r
-// request then it will change that entry to Invalid (-1)\r
-//\r
-// So to request DSD then\r
-//\r
-// ASIOIoFormat NeedThis={kASIODSDFormat};\r
-//\r
-// if(ASE_SUCCESS != ASIOFuture(kAsioSetIoFormat,&NeedThis) ){\r
-// // If the board did not accept one of the parameters then the\r
-// // whole call will fail and the failing parameter will\r
-// // have had its value changes to -1.\r
-// }\r
-//\r
-// Note: Switching between the formats need to be done before the "prepared"\r
-// state (see ASIO 2 documentation) is entered.\r
-*/\r
-typedef long int ASIOIoFormatType;\r
-enum ASIOIoFormatType_e\r
-{\r
- kASIOFormatInvalid = -1,\r
- kASIOPCMFormat = 0,\r
- kASIODSDFormat = 1,\r
-};\r
-\r
-typedef struct ASIOIoFormat_s\r
-{\r
- ASIOIoFormatType FormatType;\r
- char future[512-sizeof(ASIOIoFormatType)];\r
-} ASIOIoFormat;\r
-\r
-// Extension for drop detection\r
-// Note: Refers to buffering that goes beyond the double buffer e.g. used by USB driver designs\r
-typedef struct ASIOInternalBufferInfo\r
-{\r
- long inputSamples; // size of driver's internal input buffering which is included in getLatencies\r
- long outputSamples; // size of driver's internal output buffering which is included in getLatencies\r
-} ASIOInternalBufferInfo;\r
-\r
-\r
-ASIOError ASIOOutputReady(void);\r
-/* Purpose:\r
- this tells the driver that the host has completed processing\r
- the output buffers. if the data format required by the hardware\r
- differs from the supported asio formats, but the hardware\r
- buffers are DMA buffers, the driver will have to convert\r
- the audio stream data; as the bufferSwitch callback is\r
- usually issued at dma block switch time, the driver will\r
- have to convert the *previous* host buffer, which increases\r
- the output latency by one block.\r
- when the host finds out that ASIOOutputReady() returns\r
- true, it will issue this call whenever it completed\r
- output processing. then the driver can convert the\r
- host data directly to the dma buffer to be played next,\r
- reducing output latency by one block.\r
- another way to look at it is, that the buffer switch is called\r
- in order to pass the *input* stream to the host, so that it can\r
- process the input into the output, and the output stream is passed\r
- to the driver when the host has completed its process.\r
- Parameter:\r
- None\r
- Returns:\r
- only if the above mentioned scenario is given, and a reduction\r
- of output latency can be acheived by this mechanism, should\r
- ASE_OK be returned. otherwise (and usually), ASE_NotPresent\r
- should be returned in order to prevent further calls to this\r
- function. note that the host may want to determine if it is\r
- to use this when the system is not yet fully initialized, so\r
- ASE_OK should always be returned if the mechanism makes sense. \r
- Notes:\r
- please remeber to adjust ASIOGetLatencies() according to\r
- whether ASIOOutputReady() was ever called or not, if your\r
- driver supports this scenario.\r
- also note that the engine may fail to call ASIO_OutputReady()\r
- in time in overload cases. as already mentioned, bufferSwitch\r
- should be called for every block regardless of whether a block\r
- could be processed in time.\r
-*/\r
-\r
-// restore old alignment\r
-#if defined(_MSC_VER) && !defined(__MWERKS__) \r
-#pragma pack(pop)\r
-#elif PRAGMA_ALIGN_SUPPORTED\r
-#pragma options align = reset\r
-#endif\r
-\r
-#endif\r
-\r
+++ /dev/null
-#include <string.h>\r
-#include "asiodrivers.h"\r
-\r
-AsioDrivers* asioDrivers = 0;\r
-\r
-bool loadAsioDriver(char *name);\r
-\r
-bool loadAsioDriver(char *name)\r
-{\r
- if(!asioDrivers)\r
- asioDrivers = new AsioDrivers();\r
- if(asioDrivers)\r
- return asioDrivers->loadDriver(name);\r
- return false;\r
-}\r
-\r
-//------------------------------------------------------------------------------------\r
-\r
-#if MAC\r
-\r
-bool resolveASIO(unsigned long aconnID);\r
-\r
-AsioDrivers::AsioDrivers() : CodeFragments("ASIO Drivers", 'AsDr', 'Asio')\r
-{\r
- connID = -1;\r
- curIndex = -1;\r
-}\r
-\r
-AsioDrivers::~AsioDrivers()\r
-{\r
- removeCurrentDriver();\r
-}\r
-\r
-bool AsioDrivers::getCurrentDriverName(char *name)\r
-{\r
- if(curIndex >= 0)\r
- return getName(curIndex, name);\r
- return false;\r
-}\r
-\r
-long AsioDrivers::getDriverNames(char **names, long maxDrivers)\r
-{\r
- for(long i = 0; i < getNumFragments() && i < maxDrivers; i++)\r
- getName(i, names[i]);\r
- return getNumFragments() < maxDrivers ? getNumFragments() : maxDrivers;\r
-}\r
-\r
-bool AsioDrivers::loadDriver(char *name)\r
-{\r
- char dname[64];\r
- unsigned long newID;\r
-\r
- for(long i = 0; i < getNumFragments(); i++)\r
- {\r
- if(getName(i, dname) && !strcmp(name, dname))\r
- {\r
- if(newInstance(i, &newID))\r
- {\r
- if(resolveASIO(newID))\r
- {\r
- if(connID != -1)\r
- removeInstance(curIndex, connID);\r
- curIndex = i;\r
- connID = newID;\r
- return true;\r
- }\r
- }\r
- break;\r
- }\r
- }\r
- return false;\r
-}\r
-\r
-void AsioDrivers::removeCurrentDriver()\r
-{\r
- if(connID != -1)\r
- removeInstance(curIndex, connID);\r
- connID = -1;\r
- curIndex = -1;\r
-}\r
-\r
-//------------------------------------------------------------------------------------\r
-\r
-#elif WINDOWS\r
-\r
-#include "iasiodrv.h"\r
-\r
-extern IASIO* theAsioDriver;\r
-\r
-AsioDrivers::AsioDrivers() : AsioDriverList()\r
-{\r
- curIndex = -1;\r
-}\r
-\r
-AsioDrivers::~AsioDrivers()\r
-{\r
-}\r
-\r
-bool AsioDrivers::getCurrentDriverName(char *name)\r
-{\r
- if(curIndex >= 0)\r
- return asioGetDriverName(curIndex, name, 32) == 0 ? true : false;\r
- name[0] = 0;\r
- return false;\r
-}\r
-\r
-long AsioDrivers::getDriverNames(char **names, long maxDrivers)\r
-{\r
- for(long i = 0; i < asioGetNumDev() && i < maxDrivers; i++)\r
- asioGetDriverName(i, names[i], 32);\r
- return asioGetNumDev() < maxDrivers ? asioGetNumDev() : maxDrivers;\r
-}\r
-\r
-bool AsioDrivers::loadDriver(char *name)\r
-{\r
- char dname[64];\r
- char curName[64];\r
-\r
- for(long i = 0; i < asioGetNumDev(); i++)\r
- {\r
- if(!asioGetDriverName(i, dname, 32) && !strcmp(name, dname))\r
- {\r
- curName[0] = 0;\r
- getCurrentDriverName(curName); // in case we fail...\r
- removeCurrentDriver();\r
-\r
- if(!asioOpenDriver(i, (void **)&theAsioDriver))\r
- {\r
- curIndex = i;\r
- return true;\r
- }\r
- else\r
- {\r
- theAsioDriver = 0;\r
- if(curName[0] && strcmp(dname, curName))\r
- loadDriver(curName); // try restore\r
- }\r
- break;\r
- }\r
- }\r
- return false;\r
-}\r
-\r
-void AsioDrivers::removeCurrentDriver()\r
-{\r
- if(curIndex != -1)\r
- asioCloseDriver(curIndex);\r
- curIndex = -1;\r
-}\r
-\r
-#elif SGI || BEOS\r
-\r
-#include "asiolist.h"\r
-\r
-AsioDrivers::AsioDrivers() \r
- : AsioDriverList()\r
-{\r
- curIndex = -1;\r
-}\r
-\r
-AsioDrivers::~AsioDrivers()\r
-{\r
-}\r
-\r
-bool AsioDrivers::getCurrentDriverName(char *name)\r
-{\r
- return false;\r
-}\r
-\r
-long AsioDrivers::getDriverNames(char **names, long maxDrivers)\r
-{\r
- return 0;\r
-}\r
-\r
-bool AsioDrivers::loadDriver(char *name)\r
-{\r
- return false;\r
-}\r
-\r
-void AsioDrivers::removeCurrentDriver()\r
-{\r
-}\r
-\r
-#else\r
-#error implement me\r
-#endif\r
+++ /dev/null
-#ifndef __AsioDrivers__\r
-#define __AsioDrivers__\r
-\r
-#include "ginclude.h"\r
-\r
-#if MAC\r
-#include "CodeFragments.hpp"\r
-\r
-class AsioDrivers : public CodeFragments\r
-\r
-#elif WINDOWS\r
-#include <windows.h>\r
-#include "asiolist.h"\r
-\r
-class AsioDrivers : public AsioDriverList\r
-\r
-#elif SGI || BEOS\r
-#include "asiolist.h"\r
-\r
-class AsioDrivers : public AsioDriverList\r
-\r
-#else\r
-#error implement me\r
-#endif\r
-\r
-{\r
-public:\r
- AsioDrivers();\r
- ~AsioDrivers();\r
- \r
- bool getCurrentDriverName(char *name);\r
- long getDriverNames(char **names, long maxDrivers);\r
- bool loadDriver(char *name);\r
- void removeCurrentDriver();\r
- long getCurrentDriverIndex() {return curIndex;}\r
-protected:\r
- unsigned long connID;\r
- long curIndex;\r
-};\r
-\r
-#endif\r
+++ /dev/null
-/*\r
- Steinberg Audio Stream I/O API\r
- (c) 1996, Steinberg Soft- und Hardware GmbH\r
- charlie (May 1996)\r
-\r
- asiodrvr.h\r
- c++ superclass to implement asio functionality. from this,\r
- you can derive whatever required\r
-*/\r
-\r
-#ifndef _asiodrvr_\r
-#define _asiodrvr_\r
-\r
-// cpu and os system we are running on\r
-#include "asiosys.h"\r
-// basic "C" interface\r
-#include "asio.h"\r
-\r
-class AsioDriver;\r
-extern AsioDriver *getDriver(); // for generic constructor \r
-\r
-#if WINDOWS\r
-#include <windows.h>\r
-#include "combase.h"\r
-#include "iasiodrv.h"\r
-class AsioDriver : public IASIO ,public CUnknown\r
-{\r
-public:\r
- AsioDriver(LPUNKNOWN pUnk, HRESULT *phr);\r
-\r
- DECLARE_IUNKNOWN\r
- // Factory method\r
- static CUnknown *CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);\r
- // IUnknown\r
- virtual HRESULT STDMETHODCALLTYPE NonDelegatingQueryInterface(REFIID riid,void **ppvObject);\r
-\r
-#else\r
-\r
-class AsioDriver\r
-{\r
-public:\r
- AsioDriver();\r
-#endif\r
- virtual ~AsioDriver();\r
-\r
- virtual ASIOBool init(void* sysRef);\r
- virtual void getDriverName(char *name); // max 32 bytes incl. terminating zero\r
- virtual long getDriverVersion();\r
- virtual void getErrorMessage(char *string); // max 124 bytes incl.\r
-\r
- virtual ASIOError start();\r
- virtual ASIOError stop();\r
-\r
- virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels);\r
- virtual ASIOError getLatencies(long *inputLatency, long *outputLatency);\r
- virtual ASIOError getBufferSize(long *minSize, long *maxSize,\r
- long *preferredSize, long *granularity);\r
-\r
- virtual ASIOError canSampleRate(ASIOSampleRate sampleRate);\r
- virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate);\r
- virtual ASIOError setSampleRate(ASIOSampleRate sampleRate);\r
- virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources);\r
- virtual ASIOError setClockSource(long reference);\r
-\r
- virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp);\r
- virtual ASIOError getChannelInfo(ASIOChannelInfo *info);\r
-\r
- virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels,\r
- long bufferSize, ASIOCallbacks *callbacks);\r
- virtual ASIOError disposeBuffers();\r
-\r
- virtual ASIOError controlPanel();\r
- virtual ASIOError future(long selector, void *opt);\r
- virtual ASIOError outputReady();\r
-};\r
-#endif\r
+++ /dev/null
-#include <windows.h>
-#include "iasiodrv.h"
-#include "asiolist.h"
-
-#define ASIODRV_DESC "description"
-#define INPROC_SERVER "InprocServer32"
-#define ASIO_PATH "software\\asio"
-#define COM_CLSID "clsid"
-
-// ******************************************************************
-// Local Functions
-// ******************************************************************
-static LONG findDrvPath (char *clsidstr,char *dllpath,int dllpathsize)
-{
- HKEY hkEnum,hksub,hkpath;
- char databuf[512];
- LONG cr,rc = -1;
- DWORD datatype,datasize;
- DWORD index;
- OFSTRUCT ofs;
- HFILE hfile;
- BOOL found = FALSE;
-
-#ifdef UNICODE
- CharLowerBuffA(clsidstr,strlen(clsidstr));
- if ((cr = RegOpenKeyA(HKEY_CLASSES_ROOT,COM_CLSID,&hkEnum)) == ERROR_SUCCESS) {
-
- index = 0;
- while (cr == ERROR_SUCCESS && !found) {
- cr = RegEnumKeyA(hkEnum,index++,databuf,512);
- if (cr == ERROR_SUCCESS) {
- CharLowerBuffA(databuf,strlen(databuf));
- if (!(strcmp(databuf,clsidstr))) {
- if ((cr = RegOpenKeyExA(hkEnum,databuf,0,KEY_READ,&hksub)) == ERROR_SUCCESS) {
- if ((cr = RegOpenKeyExA(hksub,INPROC_SERVER,0,KEY_READ,&hkpath)) == ERROR_SUCCESS) {
- datatype = REG_SZ; datasize = (DWORD)dllpathsize;
- cr = RegQueryValueEx(hkpath,0,0,&datatype,(LPBYTE)dllpath,&datasize);
- if (cr == ERROR_SUCCESS) {
- memset(&ofs,0,sizeof(OFSTRUCT));
- ofs.cBytes = sizeof(OFSTRUCT);
- hfile = OpenFile(dllpath,&ofs,OF_EXIST);
- if (hfile) rc = 0;
- }
- RegCloseKey(hkpath);
- }
- RegCloseKey(hksub);
- }
- found = TRUE; // break out
- }
- }
- }
- RegCloseKey(hkEnum);
- }
-#else
- CharLowerBuff(clsidstr,strlen(clsidstr));
- if ((cr = RegOpenKey(HKEY_CLASSES_ROOT,COM_CLSID,&hkEnum)) == ERROR_SUCCESS) {
-
- index = 0;
- while (cr == ERROR_SUCCESS && !found) {
- cr = RegEnumKey(hkEnum,index++,databuf,512);
- if (cr == ERROR_SUCCESS) {
- CharLowerBuff(databuf,strlen(databuf));
- if (!(strcmp(databuf,clsidstr))) {
- if ((cr = RegOpenKeyEx(hkEnum,databuf,0,KEY_READ,&hksub)) == ERROR_SUCCESS) {
- if ((cr = RegOpenKeyEx(hksub,INPROC_SERVER,0,KEY_READ,&hkpath)) == ERROR_SUCCESS) {
- datatype = REG_SZ; datasize = (DWORD)dllpathsize;
- cr = RegQueryValueEx(hkpath,0,0,&datatype,(LPBYTE)dllpath,&datasize);
- if (cr == ERROR_SUCCESS) {
- memset(&ofs,0,sizeof(OFSTRUCT));
- ofs.cBytes = sizeof(OFSTRUCT);
- hfile = OpenFile(dllpath,&ofs,OF_EXIST);
- if (hfile) rc = 0;
- }
- RegCloseKey(hkpath);
- }
- RegCloseKey(hksub);
- }
- found = TRUE; // break out
- }
- }
- }
- RegCloseKey(hkEnum);
- }
-#endif
- return rc;
-}
-
-
-static LPASIODRVSTRUCT newDrvStruct (HKEY hkey,char *keyname,int drvID,LPASIODRVSTRUCT lpdrv)
-{
- HKEY hksub;
- char databuf[256];
- char dllpath[MAXPATHLEN];
- WORD wData[100];
- CLSID clsid;
- DWORD datatype,datasize;
- LONG cr,rc;
-
- if (!lpdrv) {
- if ((cr = RegOpenKeyExA(hkey,keyname,0,KEY_READ,&hksub)) == ERROR_SUCCESS) {
-
- datatype = REG_SZ; datasize = 256;
- cr = RegQueryValueExA(hksub,COM_CLSID,0,&datatype,(LPBYTE)databuf,&datasize);
- if (cr == ERROR_SUCCESS) {
- rc = findDrvPath (databuf,dllpath,MAXPATHLEN);
- if (rc == 0) {
- lpdrv = new ASIODRVSTRUCT[1];
- if (lpdrv) {
- memset(lpdrv,0,sizeof(ASIODRVSTRUCT));
- lpdrv->drvID = drvID;
- MultiByteToWideChar(CP_ACP,0,(LPCSTR)databuf,-1,(LPWSTR)wData,100);
- if ((cr = CLSIDFromString((LPOLESTR)wData,(LPCLSID)&clsid)) == S_OK) {
- memcpy(&lpdrv->clsid,&clsid,sizeof(CLSID));
- }
-
- datatype = REG_SZ; datasize = 256;
- cr = RegQueryValueExA(hksub,ASIODRV_DESC,0,&datatype,(LPBYTE)databuf,&datasize);
- if (cr == ERROR_SUCCESS) {
- strcpy(lpdrv->drvname,databuf);
- }
- else strcpy(lpdrv->drvname,keyname);
- }
- }
- }
- RegCloseKey(hksub);
- }
- }
- else lpdrv->next = newDrvStruct(hkey,keyname,drvID+1,lpdrv->next);
-
- return lpdrv;
-}
-
-static void deleteDrvStruct (LPASIODRVSTRUCT lpdrv)
-{
- IASIO *iasio;
-
- if (lpdrv != 0) {
- deleteDrvStruct(lpdrv->next);
- if (lpdrv->asiodrv) {
- iasio = (IASIO *)lpdrv->asiodrv;
- iasio->Release();
- }
- delete lpdrv;
- }
-}
-
-
-static LPASIODRVSTRUCT getDrvStruct (int drvID,LPASIODRVSTRUCT lpdrv)
-{
- while (lpdrv) {
- if (lpdrv->drvID == drvID) return lpdrv;
- lpdrv = lpdrv->next;
- }
- return 0;
-}
-// ******************************************************************
-
-
-// ******************************************************************
-// AsioDriverList
-// ******************************************************************
-AsioDriverList::AsioDriverList ()
-{
- HKEY hkEnum = 0;
- char keyname[MAXDRVNAMELEN];
- LPASIODRVSTRUCT pdl;
- LONG cr;
- DWORD index = 0;
- BOOL fin = FALSE;
-
- numdrv = 0;
- lpdrvlist = 0;
-
-#ifdef UNICODE
- cr = RegOpenKeyA(HKEY_LOCAL_MACHINE,ASIO_PATH,&hkEnum);
-#else
- cr = RegOpenKey(HKEY_LOCAL_MACHINE,ASIO_PATH,&hkEnum);
-#endif
- while (cr == ERROR_SUCCESS) {
-#ifdef UNICODE
- if ((cr = RegEnumKeyA(hkEnum,index++,keyname,MAXDRVNAMELEN))== ERROR_SUCCESS) {
-#else
- if ((cr = RegEnumKey(hkEnum,index++,keyname,MAXDRVNAMELEN))== ERROR_SUCCESS) {
-#endif
- lpdrvlist = newDrvStruct (hkEnum,keyname,0,lpdrvlist);
- }
- else fin = TRUE;
- }
- if (hkEnum) RegCloseKey(hkEnum);
-
- pdl = lpdrvlist;
- while (pdl) {
- numdrv++;
- pdl = pdl->next;
- }
-
- if (numdrv) CoInitialize(0); // initialize COM
-}
-
-AsioDriverList::~AsioDriverList ()
-{
- if (numdrv) {
- deleteDrvStruct(lpdrvlist);
- CoUninitialize();
- }
-}
-
-
-LONG AsioDriverList::asioGetNumDev (VOID)
-{
- return (LONG)numdrv;
-}
-
-
-LONG AsioDriverList::asioOpenDriver (int drvID,LPVOID *asiodrv)
-{
- LPASIODRVSTRUCT lpdrv = 0;
- long rc;
-
- if (!asiodrv) return DRVERR_INVALID_PARAM;
-
- if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) {
- if (!lpdrv->asiodrv) {
- rc = CoCreateInstance(lpdrv->clsid,0,CLSCTX_INPROC_SERVER,lpdrv->clsid,asiodrv);
- if (rc == S_OK) {
- lpdrv->asiodrv = *asiodrv;
- return 0;
- }
- // else if (rc == REGDB_E_CLASSNOTREG)
- // strcpy (info->messageText, "Driver not registered in the Registration Database!");
- }
- else rc = DRVERR_DEVICE_ALREADY_OPEN;
- }
- else rc = DRVERR_DEVICE_NOT_FOUND;
-
- return rc;
-}
-
-
-LONG AsioDriverList::asioCloseDriver (int drvID)
-{
- LPASIODRVSTRUCT lpdrv = 0;
- IASIO *iasio;
-
- if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) {
- if (lpdrv->asiodrv) {
- iasio = (IASIO *)lpdrv->asiodrv;
- iasio->Release();
- lpdrv->asiodrv = 0;
- }
- }
-
- return 0;
-}
-
-LONG AsioDriverList::asioGetDriverName (int drvID,char *drvname,int drvnamesize)
-{
- LPASIODRVSTRUCT lpdrv = 0;
-
- if (!drvname) return DRVERR_INVALID_PARAM;
-
- if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) {
- if (strlen(lpdrv->drvname) < (unsigned int)drvnamesize) {
- strcpy(drvname,lpdrv->drvname);
- }
- else {
- memcpy(drvname,lpdrv->drvname,drvnamesize-4);
- drvname[drvnamesize-4] = '.';
- drvname[drvnamesize-3] = '.';
- drvname[drvnamesize-2] = '.';
- drvname[drvnamesize-1] = 0;
- }
- return 0;
- }
- return DRVERR_DEVICE_NOT_FOUND;
-}
-
-LONG AsioDriverList::asioGetDriverPath (int drvID,char *dllpath,int dllpathsize)
-{
- LPASIODRVSTRUCT lpdrv = 0;
-
- if (!dllpath) return DRVERR_INVALID_PARAM;
-
- if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) {
- if (strlen(lpdrv->dllpath) < (unsigned int)dllpathsize) {
- strcpy(dllpath,lpdrv->dllpath);
- return 0;
- }
- dllpath[0] = 0;
- return DRVERR_INVALID_PARAM;
- }
- return DRVERR_DEVICE_NOT_FOUND;
-}
-
-LONG AsioDriverList::asioGetDriverCLSID (int drvID,CLSID *clsid)
-{
- LPASIODRVSTRUCT lpdrv = 0;
-
- if (!clsid) return DRVERR_INVALID_PARAM;
-
- if ((lpdrv = getDrvStruct(drvID,lpdrvlist)) != 0) {
- memcpy(clsid,&lpdrv->clsid,sizeof(CLSID));
- return 0;
- }
- return DRVERR_DEVICE_NOT_FOUND;
-}
-
-
+++ /dev/null
-#ifndef __asiolist__\r
-#define __asiolist__\r
-\r
-#define DRVERR -5000\r
-#define DRVERR_INVALID_PARAM DRVERR-1\r
-#define DRVERR_DEVICE_ALREADY_OPEN DRVERR-2\r
-#define DRVERR_DEVICE_NOT_FOUND DRVERR-3\r
-\r
-#define MAXPATHLEN 512\r
-#define MAXDRVNAMELEN 128\r
-\r
-struct asiodrvstruct\r
-{\r
- int drvID;\r
- CLSID clsid;\r
- char dllpath[MAXPATHLEN];\r
- char drvname[MAXDRVNAMELEN];\r
- LPVOID asiodrv;\r
- struct asiodrvstruct *next;\r
-};\r
-\r
-typedef struct asiodrvstruct ASIODRVSTRUCT;\r
-typedef ASIODRVSTRUCT *LPASIODRVSTRUCT;\r
-\r
-class AsioDriverList {\r
-public:\r
- AsioDriverList();\r
- ~AsioDriverList();\r
- \r
- LONG asioOpenDriver (int,VOID **);\r
- LONG asioCloseDriver (int);\r
-\r
- // nice to have\r
- LONG asioGetNumDev (VOID);\r
- LONG asioGetDriverName (int,char *,int); \r
- LONG asioGetDriverPath (int,char *,int);\r
- LONG asioGetDriverCLSID (int,CLSID *);\r
-\r
- // or use directly access\r
- LPASIODRVSTRUCT lpdrvlist;\r
- int numdrv;\r
-};\r
-\r
-typedef class AsioDriverList *LPASIODRIVERLIST;\r
-\r
-#endif\r
+++ /dev/null
-#ifndef __asiosys__\r
- #define __asiosys__\r
-\r
- #if defined(_WIN32) || defined(_WIN64)\r
- #undef MAC \r
- #define PPC 0\r
- #define WINDOWS 1\r
- #define SGI 0\r
- #define SUN 0\r
- #define LINUX 0\r
- #define BEOS 0\r
-\r
- #define NATIVE_INT64 0\r
- #define IEEE754_64FLOAT 1\r
- \r
- #elif BEOS\r
- #define MAC 0\r
- #define PPC 0\r
- #define WINDOWS 0\r
- #define PC 0\r
- #define SGI 0\r
- #define SUN 0\r
- #define LINUX 0\r
- \r
- #define NATIVE_INT64 0\r
- #define IEEE754_64FLOAT 1\r
- \r
- #ifndef DEBUG\r
- #define DEBUG 0\r
- #if DEBUG\r
- void DEBUGGERMESSAGE(char *string);\r
- #else\r
- #define DEBUGGERMESSAGE(a)\r
- #endif\r
- #endif\r
-\r
- #elif SGI\r
- #define MAC 0\r
- #define PPC 0\r
- #define WINDOWS 0\r
- #define PC 0\r
- #define SUN 0\r
- #define LINUX 0\r
- #define BEOS 0\r
- \r
- #define NATIVE_INT64 0\r
- #define IEEE754_64FLOAT 1\r
- \r
- #ifndef DEBUG\r
- #define DEBUG 0\r
- #if DEBUG\r
- void DEBUGGERMESSAGE(char *string);\r
- #else\r
- #define DEBUGGERMESSAGE(a)\r
- #endif\r
- #endif\r
-\r
- #else // MAC\r
-\r
- #define MAC 1\r
- #define PPC 1\r
- #define WINDOWS 0\r
- #define PC 0\r
- #define SGI 0\r
- #define SUN 0\r
- #define LINUX 0\r
- #define BEOS 0\r
-\r
- #define NATIVE_INT64 0\r
- #define IEEE754_64FLOAT 1\r
-\r
- #ifndef DEBUG\r
- #define DEBUG 0\r
- #if DEBUG\r
- void DEBUGGERMESSAGE(char *string);\r
- #else\r
- #define DEBUGGERMESSAGE(a)\r
- #endif\r
- #endif\r
- #endif\r
-\r
-#endif\r
+++ /dev/null
-/*==========================================================================;
- *
- * Copyright (c) Microsoft Corporation. All rights reserved.
- *
- * File: dsound.h
- * Content: DirectSound include file
- *
- **************************************************************************/
-
-#define COM_NO_WINDOWS_H
-#include <objbase.h>
-#include <float.h>
-
-#ifndef DIRECTSOUND_VERSION
-#define DIRECTSOUND_VERSION 0x0900 /* Version 9.0 */
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif // __cplusplus
-
-#ifndef __DSOUND_INCLUDED__
-#define __DSOUND_INCLUDED__
-
-/* Type definitions shared with Direct3D */
-
-#ifndef DX_SHARED_DEFINES
-
-typedef float D3DVALUE, *LPD3DVALUE;
-
-#ifndef D3DCOLOR_DEFINED
-typedef DWORD D3DCOLOR;
-#define D3DCOLOR_DEFINED
-#endif
-
-#ifndef LPD3DCOLOR_DEFINED
-typedef DWORD *LPD3DCOLOR;
-#define LPD3DCOLOR_DEFINED
-#endif
-
-#ifndef D3DVECTOR_DEFINED
-typedef struct _D3DVECTOR {
- float x;
- float y;
- float z;
-} D3DVECTOR;
-#define D3DVECTOR_DEFINED
-#endif
-
-#ifndef LPD3DVECTOR_DEFINED
-typedef D3DVECTOR *LPD3DVECTOR;
-#define LPD3DVECTOR_DEFINED
-#endif
-
-#define DX_SHARED_DEFINES
-#endif // DX_SHARED_DEFINES
-
-#define _FACDS 0x878 /* DirectSound's facility code */
-#define MAKE_DSHRESULT(code) MAKE_HRESULT(1, _FACDS, code)
-
-// DirectSound Component GUID {47D4D946-62E8-11CF-93BC-444553540000}
-DEFINE_GUID(CLSID_DirectSound, 0x47d4d946, 0x62e8, 0x11cf, 0x93, 0xbc, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0);
-
-// DirectSound 8.0 Component GUID {3901CC3F-84B5-4FA4-BA35-AA8172B8A09B}
-DEFINE_GUID(CLSID_DirectSound8, 0x3901cc3f, 0x84b5, 0x4fa4, 0xba, 0x35, 0xaa, 0x81, 0x72, 0xb8, 0xa0, 0x9b);
-
-// DirectSound Capture Component GUID {B0210780-89CD-11D0-AF08-00A0C925CD16}
-DEFINE_GUID(CLSID_DirectSoundCapture, 0xb0210780, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
-
-// DirectSound 8.0 Capture Component GUID {E4BCAC13-7F99-4908-9A8E-74E3BF24B6E1}
-DEFINE_GUID(CLSID_DirectSoundCapture8, 0xe4bcac13, 0x7f99, 0x4908, 0x9a, 0x8e, 0x74, 0xe3, 0xbf, 0x24, 0xb6, 0xe1);
-
-// DirectSound Full Duplex Component GUID {FEA4300C-7959-4147-B26A-2377B9E7A91D}
-DEFINE_GUID(CLSID_DirectSoundFullDuplex, 0xfea4300c, 0x7959, 0x4147, 0xb2, 0x6a, 0x23, 0x77, 0xb9, 0xe7, 0xa9, 0x1d);
-
-
-// DirectSound default playback device GUID {DEF00000-9C6D-47ED-AAF1-4DDA8F2B5C03}
-DEFINE_GUID(DSDEVID_DefaultPlayback, 0xdef00000, 0x9c6d, 0x47ed, 0xaa, 0xf1, 0x4d, 0xda, 0x8f, 0x2b, 0x5c, 0x03);
-
-// DirectSound default capture device GUID {DEF00001-9C6D-47ED-AAF1-4DDA8F2B5C03}
-DEFINE_GUID(DSDEVID_DefaultCapture, 0xdef00001, 0x9c6d, 0x47ed, 0xaa, 0xf1, 0x4d, 0xda, 0x8f, 0x2b, 0x5c, 0x03);
-
-// DirectSound default device for voice playback {DEF00002-9C6D-47ED-AAF1-4DDA8F2B5C03}
-DEFINE_GUID(DSDEVID_DefaultVoicePlayback, 0xdef00002, 0x9c6d, 0x47ed, 0xaa, 0xf1, 0x4d, 0xda, 0x8f, 0x2b, 0x5c, 0x03);
-
-// DirectSound default device for voice capture {DEF00003-9C6D-47ED-AAF1-4DDA8F2B5C03}
-DEFINE_GUID(DSDEVID_DefaultVoiceCapture, 0xdef00003, 0x9c6d, 0x47ed, 0xaa, 0xf1, 0x4d, 0xda, 0x8f, 0x2b, 0x5c, 0x03);
-
-
-//
-// Forward declarations for interfaces.
-// 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined
-//
-
-#ifdef __cplusplus
-struct IDirectSound;
-struct IDirectSoundBuffer;
-struct IDirectSound3DListener;
-struct IDirectSound3DBuffer;
-struct IDirectSoundCapture;
-struct IDirectSoundCaptureBuffer;
-struct IDirectSoundNotify;
-#endif // __cplusplus
-
-
-//
-// DirectSound 8.0 interfaces.
-//
-
-#if DIRECTSOUND_VERSION >= 0x0800
-
-#ifdef __cplusplus
-struct IDirectSound8;
-struct IDirectSoundBuffer8;
-struct IDirectSoundCaptureBuffer8;
-struct IDirectSoundFXGargle;
-struct IDirectSoundFXChorus;
-struct IDirectSoundFXFlanger;
-struct IDirectSoundFXEcho;
-struct IDirectSoundFXDistortion;
-struct IDirectSoundFXCompressor;
-struct IDirectSoundFXParamEq;
-struct IDirectSoundFXWavesReverb;
-struct IDirectSoundFXI3DL2Reverb;
-struct IDirectSoundCaptureFXAec;
-struct IDirectSoundCaptureFXNoiseSuppress;
-struct IDirectSoundFullDuplex;
-#endif // __cplusplus
-
-// IDirectSound8, IDirectSoundBuffer8 and IDirectSoundCaptureBuffer8 are the
-// only DirectSound 7.0 interfaces with changed functionality in version 8.0.
-// The other level 8 interfaces as equivalent to their level 7 counterparts:
-
-#define IDirectSoundCapture8 IDirectSoundCapture
-#define IDirectSound3DListener8 IDirectSound3DListener
-#define IDirectSound3DBuffer8 IDirectSound3DBuffer
-#define IDirectSoundNotify8 IDirectSoundNotify
-#define IDirectSoundFXGargle8 IDirectSoundFXGargle
-#define IDirectSoundFXChorus8 IDirectSoundFXChorus
-#define IDirectSoundFXFlanger8 IDirectSoundFXFlanger
-#define IDirectSoundFXEcho8 IDirectSoundFXEcho
-#define IDirectSoundFXDistortion8 IDirectSoundFXDistortion
-#define IDirectSoundFXCompressor8 IDirectSoundFXCompressor
-#define IDirectSoundFXParamEq8 IDirectSoundFXParamEq
-#define IDirectSoundFXWavesReverb8 IDirectSoundFXWavesReverb
-#define IDirectSoundFXI3DL2Reverb8 IDirectSoundFXI3DL2Reverb
-#define IDirectSoundCaptureFXAec8 IDirectSoundCaptureFXAec
-#define IDirectSoundCaptureFXNoiseSuppress8 IDirectSoundCaptureFXNoiseSuppress
-#define IDirectSoundFullDuplex8 IDirectSoundFullDuplex
-
-#endif // DIRECTSOUND_VERSION >= 0x0800
-
-typedef struct IDirectSound *LPDIRECTSOUND;
-typedef struct IDirectSoundBuffer *LPDIRECTSOUNDBUFFER;
-typedef struct IDirectSound3DListener *LPDIRECTSOUND3DLISTENER;
-typedef struct IDirectSound3DBuffer *LPDIRECTSOUND3DBUFFER;
-typedef struct IDirectSoundCapture *LPDIRECTSOUNDCAPTURE;
-typedef struct IDirectSoundCaptureBuffer *LPDIRECTSOUNDCAPTUREBUFFER;
-typedef struct IDirectSoundNotify *LPDIRECTSOUNDNOTIFY;
-
-
-#if DIRECTSOUND_VERSION >= 0x0800
-
-typedef struct IDirectSoundFXGargle *LPDIRECTSOUNDFXGARGLE;
-typedef struct IDirectSoundFXChorus *LPDIRECTSOUNDFXCHORUS;
-typedef struct IDirectSoundFXFlanger *LPDIRECTSOUNDFXFLANGER;
-typedef struct IDirectSoundFXEcho *LPDIRECTSOUNDFXECHO;
-typedef struct IDirectSoundFXDistortion *LPDIRECTSOUNDFXDISTORTION;
-typedef struct IDirectSoundFXCompressor *LPDIRECTSOUNDFXCOMPRESSOR;
-typedef struct IDirectSoundFXParamEq *LPDIRECTSOUNDFXPARAMEQ;
-typedef struct IDirectSoundFXWavesReverb *LPDIRECTSOUNDFXWAVESREVERB;
-typedef struct IDirectSoundFXI3DL2Reverb *LPDIRECTSOUNDFXI3DL2REVERB;
-typedef struct IDirectSoundCaptureFXAec *LPDIRECTSOUNDCAPTUREFXAEC;
-typedef struct IDirectSoundCaptureFXNoiseSuppress *LPDIRECTSOUNDCAPTUREFXNOISESUPPRESS;
-typedef struct IDirectSoundFullDuplex *LPDIRECTSOUNDFULLDUPLEX;
-
-typedef struct IDirectSound8 *LPDIRECTSOUND8;
-typedef struct IDirectSoundBuffer8 *LPDIRECTSOUNDBUFFER8;
-typedef struct IDirectSound3DListener8 *LPDIRECTSOUND3DLISTENER8;
-typedef struct IDirectSound3DBuffer8 *LPDIRECTSOUND3DBUFFER8;
-typedef struct IDirectSoundCapture8 *LPDIRECTSOUNDCAPTURE8;
-typedef struct IDirectSoundCaptureBuffer8 *LPDIRECTSOUNDCAPTUREBUFFER8;
-typedef struct IDirectSoundNotify8 *LPDIRECTSOUNDNOTIFY8;
-typedef struct IDirectSoundFXGargle8 *LPDIRECTSOUNDFXGARGLE8;
-typedef struct IDirectSoundFXChorus8 *LPDIRECTSOUNDFXCHORUS8;
-typedef struct IDirectSoundFXFlanger8 *LPDIRECTSOUNDFXFLANGER8;
-typedef struct IDirectSoundFXEcho8 *LPDIRECTSOUNDFXECHO8;
-typedef struct IDirectSoundFXDistortion8 *LPDIRECTSOUNDFXDISTORTION8;
-typedef struct IDirectSoundFXCompressor8 *LPDIRECTSOUNDFXCOMPRESSOR8;
-typedef struct IDirectSoundFXParamEq8 *LPDIRECTSOUNDFXPARAMEQ8;
-typedef struct IDirectSoundFXWavesReverb8 *LPDIRECTSOUNDFXWAVESREVERB8;
-typedef struct IDirectSoundFXI3DL2Reverb8 *LPDIRECTSOUNDFXI3DL2REVERB8;
-typedef struct IDirectSoundCaptureFXAec8 *LPDIRECTSOUNDCAPTUREFXAEC8;
-typedef struct IDirectSoundCaptureFXNoiseSuppress8 *LPDIRECTSOUNDCAPTUREFXNOISESUPPRESS8;
-typedef struct IDirectSoundFullDuplex8 *LPDIRECTSOUNDFULLDUPLEX8;
-
-#endif // DIRECTSOUND_VERSION >= 0x0800
-
-//
-// IID definitions for the unchanged DirectSound 8.0 interfaces
-//
-
-#if DIRECTSOUND_VERSION >= 0x0800
-
-#define IID_IDirectSoundCapture8 IID_IDirectSoundCapture
-#define IID_IDirectSound3DListener8 IID_IDirectSound3DListener
-#define IID_IDirectSound3DBuffer8 IID_IDirectSound3DBuffer
-#define IID_IDirectSoundNotify8 IID_IDirectSoundNotify
-#define IID_IDirectSoundFXGargle8 IID_IDirectSoundFXGargle
-#define IID_IDirectSoundFXChorus8 IID_IDirectSoundFXChorus
-#define IID_IDirectSoundFXFlanger8 IID_IDirectSoundFXFlanger
-#define IID_IDirectSoundFXEcho8 IID_IDirectSoundFXEcho
-#define IID_IDirectSoundFXDistortion8 IID_IDirectSoundFXDistortion
-#define IID_IDirectSoundFXCompressor8 IID_IDirectSoundFXCompressor
-#define IID_IDirectSoundFXParamEq8 IID_IDirectSoundFXParamEq
-#define IID_IDirectSoundFXWavesReverb8 IID_IDirectSoundFXWavesReverb
-#define IID_IDirectSoundFXI3DL2Reverb8 IID_IDirectSoundFXI3DL2Reverb
-#define IID_IDirectSoundCaptureFXAec8 IID_IDirectSoundCaptureFXAec
-#define IID_IDirectSoundCaptureFXNoiseSuppress8 IID_IDirectSoundCaptureFXNoiseSuppress
-#define IID_IDirectSoundFullDuplex8 IID_IDirectSoundFullDuplex
-
-#endif // DIRECTSOUND_VERSION >= 0x0800
-
-//
-// Compatibility typedefs
-//
-
-#ifndef _LPCWAVEFORMATEX_DEFINED
-#define _LPCWAVEFORMATEX_DEFINED
-typedef const WAVEFORMATEX *LPCWAVEFORMATEX;
-#endif // _LPCWAVEFORMATEX_DEFINED
-
-#ifndef __LPCGUID_DEFINED__
-#define __LPCGUID_DEFINED__
-typedef const GUID *LPCGUID;
-#endif // __LPCGUID_DEFINED__
-
-typedef LPDIRECTSOUND *LPLPDIRECTSOUND;
-typedef LPDIRECTSOUNDBUFFER *LPLPDIRECTSOUNDBUFFER;
-typedef LPDIRECTSOUND3DLISTENER *LPLPDIRECTSOUND3DLISTENER;
-typedef LPDIRECTSOUND3DBUFFER *LPLPDIRECTSOUND3DBUFFER;
-typedef LPDIRECTSOUNDCAPTURE *LPLPDIRECTSOUNDCAPTURE;
-typedef LPDIRECTSOUNDCAPTUREBUFFER *LPLPDIRECTSOUNDCAPTUREBUFFER;
-typedef LPDIRECTSOUNDNOTIFY *LPLPDIRECTSOUNDNOTIFY;
-
-#if DIRECTSOUND_VERSION >= 0x0800
-typedef LPDIRECTSOUND8 *LPLPDIRECTSOUND8;
-typedef LPDIRECTSOUNDBUFFER8 *LPLPDIRECTSOUNDBUFFER8;
-typedef LPDIRECTSOUNDCAPTURE8 *LPLPDIRECTSOUNDCAPTURE8;
-typedef LPDIRECTSOUNDCAPTUREBUFFER8 *LPLPDIRECTSOUNDCAPTUREBUFFER8;
-#endif // DIRECTSOUND_VERSION >= 0x0800
-
-//
-// Structures
-//
-
-typedef struct _DSCAPS
-{
- DWORD dwSize;
- DWORD dwFlags;
- DWORD dwMinSecondarySampleRate;
- DWORD dwMaxSecondarySampleRate;
- DWORD dwPrimaryBuffers;
- DWORD dwMaxHwMixingAllBuffers;
- DWORD dwMaxHwMixingStaticBuffers;
- DWORD dwMaxHwMixingStreamingBuffers;
- DWORD dwFreeHwMixingAllBuffers;
- DWORD dwFreeHwMixingStaticBuffers;
- DWORD dwFreeHwMixingStreamingBuffers;
- DWORD dwMaxHw3DAllBuffers;
- DWORD dwMaxHw3DStaticBuffers;
- DWORD dwMaxHw3DStreamingBuffers;
- DWORD dwFreeHw3DAllBuffers;
- DWORD dwFreeHw3DStaticBuffers;
- DWORD dwFreeHw3DStreamingBuffers;
- DWORD dwTotalHwMemBytes;
- DWORD dwFreeHwMemBytes;
- DWORD dwMaxContigFreeHwMemBytes;
- DWORD dwUnlockTransferRateHwBuffers;
- DWORD dwPlayCpuOverheadSwBuffers;
- DWORD dwReserved1;
- DWORD dwReserved2;
-} DSCAPS, *LPDSCAPS;
-
-typedef const DSCAPS *LPCDSCAPS;
-
-typedef struct _DSBCAPS
-{
- DWORD dwSize;
- DWORD dwFlags;
- DWORD dwBufferBytes;
- DWORD dwUnlockTransferRate;
- DWORD dwPlayCpuOverhead;
-} DSBCAPS, *LPDSBCAPS;
-
-typedef const DSBCAPS *LPCDSBCAPS;
-
-#if DIRECTSOUND_VERSION >= 0x0800
-
- typedef struct _DSEFFECTDESC
- {
- DWORD dwSize;
- DWORD dwFlags;
- GUID guidDSFXClass;
- DWORD_PTR dwReserved1;
- DWORD_PTR dwReserved2;
- } DSEFFECTDESC, *LPDSEFFECTDESC;
- typedef const DSEFFECTDESC *LPCDSEFFECTDESC;
-
- #define DSFX_LOCHARDWARE 0x00000001
- #define DSFX_LOCSOFTWARE 0x00000002
-
- enum
- {
- DSFXR_PRESENT, // 0
- DSFXR_LOCHARDWARE, // 1
- DSFXR_LOCSOFTWARE, // 2
- DSFXR_UNALLOCATED, // 3
- DSFXR_FAILED, // 4
- DSFXR_UNKNOWN, // 5
- DSFXR_SENDLOOP // 6
- };
-
- typedef struct _DSCEFFECTDESC
- {
- DWORD dwSize;
- DWORD dwFlags;
- GUID guidDSCFXClass;
- GUID guidDSCFXInstance;
- DWORD dwReserved1;
- DWORD dwReserved2;
- } DSCEFFECTDESC, *LPDSCEFFECTDESC;
- typedef const DSCEFFECTDESC *LPCDSCEFFECTDESC;
-
- #define DSCFX_LOCHARDWARE 0x00000001
- #define DSCFX_LOCSOFTWARE 0x00000002
-
- #define DSCFXR_LOCHARDWARE 0x00000010
- #define DSCFXR_LOCSOFTWARE 0x00000020
-
-#endif // DIRECTSOUND_VERSION >= 0x0800
-
-typedef struct _DSBUFFERDESC
-{
- DWORD dwSize;
- DWORD dwFlags;
- DWORD dwBufferBytes;
- DWORD dwReserved;
- LPWAVEFORMATEX lpwfxFormat;
-#if DIRECTSOUND_VERSION >= 0x0700
- GUID guid3DAlgorithm;
-#endif
-} DSBUFFERDESC, *LPDSBUFFERDESC;
-
-typedef const DSBUFFERDESC *LPCDSBUFFERDESC;
-
-// Older version of this structure:
-
-typedef struct _DSBUFFERDESC1
-{
- DWORD dwSize;
- DWORD dwFlags;
- DWORD dwBufferBytes;
- DWORD dwReserved;
- LPWAVEFORMATEX lpwfxFormat;
-} DSBUFFERDESC1, *LPDSBUFFERDESC1;
-
-typedef const DSBUFFERDESC1 *LPCDSBUFFERDESC1;
-
-typedef struct _DS3DBUFFER
-{
- DWORD dwSize;
- D3DVECTOR vPosition;
- D3DVECTOR vVelocity;
- DWORD dwInsideConeAngle;
- DWORD dwOutsideConeAngle;
- D3DVECTOR vConeOrientation;
- LONG lConeOutsideVolume;
- D3DVALUE flMinDistance;
- D3DVALUE flMaxDistance;
- DWORD dwMode;
-} DS3DBUFFER, *LPDS3DBUFFER;
-
-typedef const DS3DBUFFER *LPCDS3DBUFFER;
-
-typedef struct _DS3DLISTENER
-{
- DWORD dwSize;
- D3DVECTOR vPosition;
- D3DVECTOR vVelocity;
- D3DVECTOR vOrientFront;
- D3DVECTOR vOrientTop;
- D3DVALUE flDistanceFactor;
- D3DVALUE flRolloffFactor;
- D3DVALUE flDopplerFactor;
-} DS3DLISTENER, *LPDS3DLISTENER;
-
-typedef const DS3DLISTENER *LPCDS3DLISTENER;
-
-typedef struct _DSCCAPS
-{
- DWORD dwSize;
- DWORD dwFlags;
- DWORD dwFormats;
- DWORD dwChannels;
-} DSCCAPS, *LPDSCCAPS;
-
-typedef const DSCCAPS *LPCDSCCAPS;
-
-typedef struct _DSCBUFFERDESC1
-{
- DWORD dwSize;
- DWORD dwFlags;
- DWORD dwBufferBytes;
- DWORD dwReserved;
- LPWAVEFORMATEX lpwfxFormat;
-} DSCBUFFERDESC1, *LPDSCBUFFERDESC1;
-
-typedef struct _DSCBUFFERDESC
-{
- DWORD dwSize;
- DWORD dwFlags;
- DWORD dwBufferBytes;
- DWORD dwReserved;
- LPWAVEFORMATEX lpwfxFormat;
-#if DIRECTSOUND_VERSION >= 0x0800
- DWORD dwFXCount;
- LPDSCEFFECTDESC lpDSCFXDesc;
-#endif
-} DSCBUFFERDESC, *LPDSCBUFFERDESC;
-
-typedef const DSCBUFFERDESC *LPCDSCBUFFERDESC;
-
-typedef struct _DSCBCAPS
-{
- DWORD dwSize;
- DWORD dwFlags;
- DWORD dwBufferBytes;
- DWORD dwReserved;
-} DSCBCAPS, *LPDSCBCAPS;
-
-typedef const DSCBCAPS *LPCDSCBCAPS;
-
-typedef struct _DSBPOSITIONNOTIFY
-{
- DWORD dwOffset;
- HANDLE hEventNotify;
-} DSBPOSITIONNOTIFY, *LPDSBPOSITIONNOTIFY;
-
-typedef const DSBPOSITIONNOTIFY *LPCDSBPOSITIONNOTIFY;
-
-//
-// DirectSound API
-//
-
-typedef BOOL (CALLBACK *LPDSENUMCALLBACKA)(LPGUID, LPCSTR, LPCSTR, LPVOID);
-typedef BOOL (CALLBACK *LPDSENUMCALLBACKW)(LPGUID, LPCWSTR, LPCWSTR, LPVOID);
-
-extern HRESULT WINAPI DirectSoundCreate(LPCGUID pcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter);
-extern HRESULT WINAPI DirectSoundEnumerateA(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
-extern HRESULT WINAPI DirectSoundEnumerateW(LPDSENUMCALLBACKW pDSEnumCallback, LPVOID pContext);
-
-extern HRESULT WINAPI DirectSoundCaptureCreate(LPCGUID pcGuidDevice, LPDIRECTSOUNDCAPTURE *ppDSC, LPUNKNOWN pUnkOuter);
-extern HRESULT WINAPI DirectSoundCaptureEnumerateA(LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext);
-extern HRESULT WINAPI DirectSoundCaptureEnumerateW(LPDSENUMCALLBACKW pDSEnumCallback, LPVOID pContext);
-
-#if DIRECTSOUND_VERSION >= 0x0800
-extern HRESULT WINAPI DirectSoundCreate8(LPCGUID pcGuidDevice, LPDIRECTSOUND8 *ppDS8, LPUNKNOWN pUnkOuter);
-extern HRESULT WINAPI DirectSoundCaptureCreate8(LPCGUID pcGuidDevice, LPDIRECTSOUNDCAPTURE8 *ppDSC8, LPUNKNOWN pUnkOuter);
-extern HRESULT WINAPI DirectSoundFullDuplexCreate(LPCGUID pcGuidCaptureDevice, LPCGUID pcGuidRenderDevice,
- LPCDSCBUFFERDESC pcDSCBufferDesc, LPCDSBUFFERDESC pcDSBufferDesc, HWND hWnd,
- DWORD dwLevel, LPDIRECTSOUNDFULLDUPLEX* ppDSFD, LPDIRECTSOUNDCAPTUREBUFFER8 *ppDSCBuffer8,
- LPDIRECTSOUNDBUFFER8 *ppDSBuffer8, LPUNKNOWN pUnkOuter);
-#define DirectSoundFullDuplexCreate8 DirectSoundFullDuplexCreate
-
-extern HRESULT WINAPI GetDeviceID(LPCGUID pGuidSrc, LPGUID pGuidDest);
-#endif // DIRECTSOUND_VERSION >= 0x0800
-
-#ifdef UNICODE
-#define LPDSENUMCALLBACK LPDSENUMCALLBACKW
-#define DirectSoundEnumerate DirectSoundEnumerateW
-#define DirectSoundCaptureEnumerate DirectSoundCaptureEnumerateW
-#else // UNICODE
-#define LPDSENUMCALLBACK LPDSENUMCALLBACKA
-#define DirectSoundEnumerate DirectSoundEnumerateA
-#define DirectSoundCaptureEnumerate DirectSoundCaptureEnumerateA
-#endif // UNICODE
-
-//
-// IUnknown
-//
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#ifndef IUnknown_QueryInterface
-#define IUnknown_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
-#endif // IUnknown_QueryInterface
-#ifndef IUnknown_AddRef
-#define IUnknown_AddRef(p) (p)->lpVtbl->AddRef(p)
-#endif // IUnknown_AddRef
-#ifndef IUnknown_Release
-#define IUnknown_Release(p) (p)->lpVtbl->Release(p)
-#endif // IUnknown_Release
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#ifndef IUnknown_QueryInterface
-#define IUnknown_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
-#endif // IUnknown_QueryInterface
-#ifndef IUnknown_AddRef
-#define IUnknown_AddRef(p) (p)->AddRef()
-#endif // IUnknown_AddRef
-#ifndef IUnknown_Release
-#define IUnknown_Release(p) (p)->Release()
-#endif // IUnknown_Release
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-#ifndef __IReferenceClock_INTERFACE_DEFINED__
-#define __IReferenceClock_INTERFACE_DEFINED__
-
-typedef LONGLONG REFERENCE_TIME;
-typedef REFERENCE_TIME *LPREFERENCE_TIME;
-
-DEFINE_GUID(IID_IReferenceClock, 0x56a86897, 0x0ad4, 0x11ce, 0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);
-
-#undef INTERFACE
-#define INTERFACE IReferenceClock
-
-DECLARE_INTERFACE_(IReferenceClock, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IReferenceClock methods
- STDMETHOD(GetTime) (THIS_ REFERENCE_TIME *pTime) PURE;
- STDMETHOD(AdviseTime) (THIS_ REFERENCE_TIME rtBaseTime, REFERENCE_TIME rtStreamTime,
- HANDLE hEvent, LPDWORD pdwAdviseCookie) PURE;
- STDMETHOD(AdvisePeriodic) (THIS_ REFERENCE_TIME rtStartTime, REFERENCE_TIME rtPeriodTime,
- HANDLE hSemaphore, LPDWORD pdwAdviseCookie) PURE;
- STDMETHOD(Unadvise) (THIS_ DWORD dwAdviseCookie) PURE;
-};
-
-#endif // __IReferenceClock_INTERFACE_DEFINED__
-
-#ifndef IReferenceClock_QueryInterface
-
-#define IReferenceClock_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IReferenceClock_AddRef(p) IUnknown_AddRef(p)
-#define IReferenceClock_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IReferenceClock_GetTime(p,a) (p)->lpVtbl->GetTime(p,a)
-#define IReferenceClock_AdviseTime(p,a,b,c,d) (p)->lpVtbl->AdviseTime(p,a,b,c,d)
-#define IReferenceClock_AdvisePeriodic(p,a,b,c,d) (p)->lpVtbl->AdvisePeriodic(p,a,b,c,d)
-#define IReferenceClock_Unadvise(p,a) (p)->lpVtbl->Unadvise(p,a)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IReferenceClock_GetTime(p,a) (p)->GetTime(a)
-#define IReferenceClock_AdviseTime(p,a,b,c,d) (p)->AdviseTime(a,b,c,d)
-#define IReferenceClock_AdvisePeriodic(p,a,b,c,d) (p)->AdvisePeriodic(a,b,c,d)
-#define IReferenceClock_Unadvise(p,a) (p)->Unadvise(a)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-#endif // IReferenceClock_QueryInterface
-
-//
-// IDirectSound
-//
-
-DEFINE_GUID(IID_IDirectSound, 0x279AFA83, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60);
-
-#undef INTERFACE
-#define INTERFACE IDirectSound
-
-DECLARE_INTERFACE_(IDirectSound, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSound methods
- STDMETHOD(CreateSoundBuffer) (THIS_ LPCDSBUFFERDESC pcDSBufferDesc, LPDIRECTSOUNDBUFFER *ppDSBuffer, LPUNKNOWN pUnkOuter) PURE;
- STDMETHOD(GetCaps) (THIS_ LPDSCAPS pDSCaps) PURE;
- STDMETHOD(DuplicateSoundBuffer) (THIS_ LPDIRECTSOUNDBUFFER pDSBufferOriginal, LPDIRECTSOUNDBUFFER *ppDSBufferDuplicate) PURE;
- STDMETHOD(SetCooperativeLevel) (THIS_ HWND hwnd, DWORD dwLevel) PURE;
- STDMETHOD(Compact) (THIS) PURE;
- STDMETHOD(GetSpeakerConfig) (THIS_ LPDWORD pdwSpeakerConfig) PURE;
- STDMETHOD(SetSpeakerConfig) (THIS_ DWORD dwSpeakerConfig) PURE;
- STDMETHOD(Initialize) (THIS_ LPCGUID pcGuidDevice) PURE;
-};
-
-#define IDirectSound_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSound_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSound_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSound_CreateSoundBuffer(p,a,b,c) (p)->lpVtbl->CreateSoundBuffer(p,a,b,c)
-#define IDirectSound_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a)
-#define IDirectSound_DuplicateSoundBuffer(p,a,b) (p)->lpVtbl->DuplicateSoundBuffer(p,a,b)
-#define IDirectSound_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b)
-#define IDirectSound_Compact(p) (p)->lpVtbl->Compact(p)
-#define IDirectSound_GetSpeakerConfig(p,a) (p)->lpVtbl->GetSpeakerConfig(p,a)
-#define IDirectSound_SetSpeakerConfig(p,b) (p)->lpVtbl->SetSpeakerConfig(p,b)
-#define IDirectSound_Initialize(p,a) (p)->lpVtbl->Initialize(p,a)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSound_CreateSoundBuffer(p,a,b,c) (p)->CreateSoundBuffer(a,b,c)
-#define IDirectSound_GetCaps(p,a) (p)->GetCaps(a)
-#define IDirectSound_DuplicateSoundBuffer(p,a,b) (p)->DuplicateSoundBuffer(a,b)
-#define IDirectSound_SetCooperativeLevel(p,a,b) (p)->SetCooperativeLevel(a,b)
-#define IDirectSound_Compact(p) (p)->Compact()
-#define IDirectSound_GetSpeakerConfig(p,a) (p)->GetSpeakerConfig(a)
-#define IDirectSound_SetSpeakerConfig(p,b) (p)->SetSpeakerConfig(b)
-#define IDirectSound_Initialize(p,a) (p)->Initialize(a)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-#if DIRECTSOUND_VERSION >= 0x0800
-
-//
-// IDirectSound8
-//
-
-DEFINE_GUID(IID_IDirectSound8, 0xC50A7E93, 0xF395, 0x4834, 0x9E, 0xF6, 0x7F, 0xA9, 0x9D, 0xE5, 0x09, 0x66);
-
-#undef INTERFACE
-#define INTERFACE IDirectSound8
-
-DECLARE_INTERFACE_(IDirectSound8, IDirectSound)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSound methods
- STDMETHOD(CreateSoundBuffer) (THIS_ LPCDSBUFFERDESC pcDSBufferDesc, LPDIRECTSOUNDBUFFER *ppDSBuffer, LPUNKNOWN pUnkOuter) PURE;
- STDMETHOD(GetCaps) (THIS_ LPDSCAPS pDSCaps) PURE;
- STDMETHOD(DuplicateSoundBuffer) (THIS_ LPDIRECTSOUNDBUFFER pDSBufferOriginal, LPDIRECTSOUNDBUFFER *ppDSBufferDuplicate) PURE;
- STDMETHOD(SetCooperativeLevel) (THIS_ HWND hwnd, DWORD dwLevel) PURE;
- STDMETHOD(Compact) (THIS) PURE;
- STDMETHOD(GetSpeakerConfig) (THIS_ LPDWORD pdwSpeakerConfig) PURE;
- STDMETHOD(SetSpeakerConfig) (THIS_ DWORD dwSpeakerConfig) PURE;
- STDMETHOD(Initialize) (THIS_ LPCGUID pcGuidDevice) PURE;
-
- // IDirectSound8 methods
- STDMETHOD(VerifyCertification) (THIS_ LPDWORD pdwCertified) PURE;
-};
-
-#define IDirectSound8_QueryInterface(p,a,b) IDirectSound_QueryInterface(p,a,b)
-#define IDirectSound8_AddRef(p) IDirectSound_AddRef(p)
-#define IDirectSound8_Release(p) IDirectSound_Release(p)
-#define IDirectSound8_CreateSoundBuffer(p,a,b,c) IDirectSound_CreateSoundBuffer(p,a,b,c)
-#define IDirectSound8_GetCaps(p,a) IDirectSound_GetCaps(p,a)
-#define IDirectSound8_DuplicateSoundBuffer(p,a,b) IDirectSound_DuplicateSoundBuffer(p,a,b)
-#define IDirectSound8_SetCooperativeLevel(p,a,b) IDirectSound_SetCooperativeLevel(p,a,b)
-#define IDirectSound8_Compact(p) IDirectSound_Compact(p)
-#define IDirectSound8_GetSpeakerConfig(p,a) IDirectSound_GetSpeakerConfig(p,a)
-#define IDirectSound8_SetSpeakerConfig(p,a) IDirectSound_SetSpeakerConfig(p,a)
-#define IDirectSound8_Initialize(p,a) IDirectSound_Initialize(p,a)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSound8_VerifyCertification(p,a) (p)->lpVtbl->VerifyCertification(p,a)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSound8_VerifyCertification(p,a) (p)->VerifyCertification(a)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-#endif // DIRECTSOUND_VERSION >= 0x0800
-
-//
-// IDirectSoundBuffer
-//
-
-DEFINE_GUID(IID_IDirectSoundBuffer, 0x279AFA85, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60);
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundBuffer
-
-DECLARE_INTERFACE_(IDirectSoundBuffer, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundBuffer methods
- STDMETHOD(GetCaps) (THIS_ LPDSBCAPS pDSBufferCaps) PURE;
- STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD pdwCurrentPlayCursor, LPDWORD pdwCurrentWriteCursor) PURE;
- STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX pwfxFormat, DWORD dwSizeAllocated, LPDWORD pdwSizeWritten) PURE;
- STDMETHOD(GetVolume) (THIS_ LPLONG plVolume) PURE;
- STDMETHOD(GetPan) (THIS_ LPLONG plPan) PURE;
- STDMETHOD(GetFrequency) (THIS_ LPDWORD pdwFrequency) PURE;
- STDMETHOD(GetStatus) (THIS_ LPDWORD pdwStatus) PURE;
- STDMETHOD(Initialize) (THIS_ LPDIRECTSOUND pDirectSound, LPCDSBUFFERDESC pcDSBufferDesc) PURE;
- STDMETHOD(Lock) (THIS_ DWORD dwOffset, DWORD dwBytes, LPVOID *ppvAudioPtr1, LPDWORD pdwAudioBytes1,
- LPVOID *ppvAudioPtr2, LPDWORD pdwAudioBytes2, DWORD dwFlags) PURE;
- STDMETHOD(Play) (THIS_ DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) PURE;
- STDMETHOD(SetCurrentPosition) (THIS_ DWORD dwNewPosition) PURE;
- STDMETHOD(SetFormat) (THIS_ LPCWAVEFORMATEX pcfxFormat) PURE;
- STDMETHOD(SetVolume) (THIS_ LONG lVolume) PURE;
- STDMETHOD(SetPan) (THIS_ LONG lPan) PURE;
- STDMETHOD(SetFrequency) (THIS_ DWORD dwFrequency) PURE;
- STDMETHOD(Stop) (THIS) PURE;
- STDMETHOD(Unlock) (THIS_ LPVOID pvAudioPtr1, DWORD dwAudioBytes1, LPVOID pvAudioPtr2, DWORD dwAudioBytes2) PURE;
- STDMETHOD(Restore) (THIS) PURE;
-};
-
-#define IDirectSoundBuffer_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundBuffer_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundBuffer_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundBuffer_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a)
-#define IDirectSoundBuffer_GetCurrentPosition(p,a,b) (p)->lpVtbl->GetCurrentPosition(p,a,b)
-#define IDirectSoundBuffer_GetFormat(p,a,b,c) (p)->lpVtbl->GetFormat(p,a,b,c)
-#define IDirectSoundBuffer_GetVolume(p,a) (p)->lpVtbl->GetVolume(p,a)
-#define IDirectSoundBuffer_GetPan(p,a) (p)->lpVtbl->GetPan(p,a)
-#define IDirectSoundBuffer_GetFrequency(p,a) (p)->lpVtbl->GetFrequency(p,a)
-#define IDirectSoundBuffer_GetStatus(p,a) (p)->lpVtbl->GetStatus(p,a)
-#define IDirectSoundBuffer_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b)
-#define IDirectSoundBuffer_Lock(p,a,b,c,d,e,f,g) (p)->lpVtbl->Lock(p,a,b,c,d,e,f,g)
-#define IDirectSoundBuffer_Play(p,a,b,c) (p)->lpVtbl->Play(p,a,b,c)
-#define IDirectSoundBuffer_SetCurrentPosition(p,a) (p)->lpVtbl->SetCurrentPosition(p,a)
-#define IDirectSoundBuffer_SetFormat(p,a) (p)->lpVtbl->SetFormat(p,a)
-#define IDirectSoundBuffer_SetVolume(p,a) (p)->lpVtbl->SetVolume(p,a)
-#define IDirectSoundBuffer_SetPan(p,a) (p)->lpVtbl->SetPan(p,a)
-#define IDirectSoundBuffer_SetFrequency(p,a) (p)->lpVtbl->SetFrequency(p,a)
-#define IDirectSoundBuffer_Stop(p) (p)->lpVtbl->Stop(p)
-#define IDirectSoundBuffer_Unlock(p,a,b,c,d) (p)->lpVtbl->Unlock(p,a,b,c,d)
-#define IDirectSoundBuffer_Restore(p) (p)->lpVtbl->Restore(p)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundBuffer_GetCaps(p,a) (p)->GetCaps(a)
-#define IDirectSoundBuffer_GetCurrentPosition(p,a,b) (p)->GetCurrentPosition(a,b)
-#define IDirectSoundBuffer_GetFormat(p,a,b,c) (p)->GetFormat(a,b,c)
-#define IDirectSoundBuffer_GetVolume(p,a) (p)->GetVolume(a)
-#define IDirectSoundBuffer_GetPan(p,a) (p)->GetPan(a)
-#define IDirectSoundBuffer_GetFrequency(p,a) (p)->GetFrequency(a)
-#define IDirectSoundBuffer_GetStatus(p,a) (p)->GetStatus(a)
-#define IDirectSoundBuffer_Initialize(p,a,b) (p)->Initialize(a,b)
-#define IDirectSoundBuffer_Lock(p,a,b,c,d,e,f,g) (p)->Lock(a,b,c,d,e,f,g)
-#define IDirectSoundBuffer_Play(p,a,b,c) (p)->Play(a,b,c)
-#define IDirectSoundBuffer_SetCurrentPosition(p,a) (p)->SetCurrentPosition(a)
-#define IDirectSoundBuffer_SetFormat(p,a) (p)->SetFormat(a)
-#define IDirectSoundBuffer_SetVolume(p,a) (p)->SetVolume(a)
-#define IDirectSoundBuffer_SetPan(p,a) (p)->SetPan(a)
-#define IDirectSoundBuffer_SetFrequency(p,a) (p)->SetFrequency(a)
-#define IDirectSoundBuffer_Stop(p) (p)->Stop()
-#define IDirectSoundBuffer_Unlock(p,a,b,c,d) (p)->Unlock(a,b,c,d)
-#define IDirectSoundBuffer_Restore(p) (p)->Restore()
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-#if DIRECTSOUND_VERSION >= 0x0800
-
-//
-// IDirectSoundBuffer8
-//
-
-DEFINE_GUID(IID_IDirectSoundBuffer8, 0x6825a449, 0x7524, 0x4d82, 0x92, 0x0f, 0x50, 0xe3, 0x6a, 0xb3, 0xab, 0x1e);
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundBuffer8
-
-DECLARE_INTERFACE_(IDirectSoundBuffer8, IDirectSoundBuffer)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundBuffer methods
- STDMETHOD(GetCaps) (THIS_ LPDSBCAPS pDSBufferCaps) PURE;
- STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD pdwCurrentPlayCursor, LPDWORD pdwCurrentWriteCursor) PURE;
- STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX pwfxFormat, DWORD dwSizeAllocated, LPDWORD pdwSizeWritten) PURE;
- STDMETHOD(GetVolume) (THIS_ LPLONG plVolume) PURE;
- STDMETHOD(GetPan) (THIS_ LPLONG plPan) PURE;
- STDMETHOD(GetFrequency) (THIS_ LPDWORD pdwFrequency) PURE;
- STDMETHOD(GetStatus) (THIS_ LPDWORD pdwStatus) PURE;
- STDMETHOD(Initialize) (THIS_ LPDIRECTSOUND pDirectSound, LPCDSBUFFERDESC pcDSBufferDesc) PURE;
- STDMETHOD(Lock) (THIS_ DWORD dwOffset, DWORD dwBytes, LPVOID *ppvAudioPtr1, LPDWORD pdwAudioBytes1,
- LPVOID *ppvAudioPtr2, LPDWORD pdwAudioBytes2, DWORD dwFlags) PURE;
- STDMETHOD(Play) (THIS_ DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) PURE;
- STDMETHOD(SetCurrentPosition) (THIS_ DWORD dwNewPosition) PURE;
- STDMETHOD(SetFormat) (THIS_ LPCWAVEFORMATEX pcfxFormat) PURE;
- STDMETHOD(SetVolume) (THIS_ LONG lVolume) PURE;
- STDMETHOD(SetPan) (THIS_ LONG lPan) PURE;
- STDMETHOD(SetFrequency) (THIS_ DWORD dwFrequency) PURE;
- STDMETHOD(Stop) (THIS) PURE;
- STDMETHOD(Unlock) (THIS_ LPVOID pvAudioPtr1, DWORD dwAudioBytes1, LPVOID pvAudioPtr2, DWORD dwAudioBytes2) PURE;
- STDMETHOD(Restore) (THIS) PURE;
-
- // IDirectSoundBuffer8 methods
- STDMETHOD(SetFX) (THIS_ DWORD dwEffectsCount, LPDSEFFECTDESC pDSFXDesc, LPDWORD pdwResultCodes) PURE;
- STDMETHOD(AcquireResources) (THIS_ DWORD dwFlags, DWORD dwEffectsCount, LPDWORD pdwResultCodes) PURE;
- STDMETHOD(GetObjectInPath) (THIS_ REFGUID rguidObject, DWORD dwIndex, REFGUID rguidInterface, LPVOID *ppObject) PURE;
-};
-
-// Special GUID meaning "select all objects" for use in GetObjectInPath()
-DEFINE_GUID(GUID_All_Objects, 0xaa114de5, 0xc262, 0x4169, 0xa1, 0xc8, 0x23, 0xd6, 0x98, 0xcc, 0x73, 0xb5);
-
-#define IDirectSoundBuffer8_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundBuffer8_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundBuffer8_Release(p) IUnknown_Release(p)
-
-#define IDirectSoundBuffer8_GetCaps(p,a) IDirectSoundBuffer_GetCaps(p,a)
-#define IDirectSoundBuffer8_GetCurrentPosition(p,a,b) IDirectSoundBuffer_GetCurrentPosition(p,a,b)
-#define IDirectSoundBuffer8_GetFormat(p,a,b,c) IDirectSoundBuffer_GetFormat(p,a,b,c)
-#define IDirectSoundBuffer8_GetVolume(p,a) IDirectSoundBuffer_GetVolume(p,a)
-#define IDirectSoundBuffer8_GetPan(p,a) IDirectSoundBuffer_GetPan(p,a)
-#define IDirectSoundBuffer8_GetFrequency(p,a) IDirectSoundBuffer_GetFrequency(p,a)
-#define IDirectSoundBuffer8_GetStatus(p,a) IDirectSoundBuffer_GetStatus(p,a)
-#define IDirectSoundBuffer8_Initialize(p,a,b) IDirectSoundBuffer_Initialize(p,a,b)
-#define IDirectSoundBuffer8_Lock(p,a,b,c,d,e,f,g) IDirectSoundBuffer_Lock(p,a,b,c,d,e,f,g)
-#define IDirectSoundBuffer8_Play(p,a,b,c) IDirectSoundBuffer_Play(p,a,b,c)
-#define IDirectSoundBuffer8_SetCurrentPosition(p,a) IDirectSoundBuffer_SetCurrentPosition(p,a)
-#define IDirectSoundBuffer8_SetFormat(p,a) IDirectSoundBuffer_SetFormat(p,a)
-#define IDirectSoundBuffer8_SetVolume(p,a) IDirectSoundBuffer_SetVolume(p,a)
-#define IDirectSoundBuffer8_SetPan(p,a) IDirectSoundBuffer_SetPan(p,a)
-#define IDirectSoundBuffer8_SetFrequency(p,a) IDirectSoundBuffer_SetFrequency(p,a)
-#define IDirectSoundBuffer8_Stop(p) IDirectSoundBuffer_Stop(p)
-#define IDirectSoundBuffer8_Unlock(p,a,b,c,d) IDirectSoundBuffer_Unlock(p,a,b,c,d)
-#define IDirectSoundBuffer8_Restore(p) IDirectSoundBuffer_Restore(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundBuffer8_SetFX(p,a,b,c) (p)->lpVtbl->SetFX(p,a,b,c)
-#define IDirectSoundBuffer8_AcquireResources(p,a,b,c) (p)->lpVtbl->AcquireResources(p,a,b,c)
-#define IDirectSoundBuffer8_GetObjectInPath(p,a,b,c,d) (p)->lpVtbl->GetObjectInPath(p,a,b,c,d)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundBuffer8_SetFX(p,a,b,c) (p)->SetFX(a,b,c)
-#define IDirectSoundBuffer8_AcquireResources(p,a,b,c) (p)->AcquireResources(a,b,c)
-#define IDirectSoundBuffer8_GetObjectInPath(p,a,b,c,d) (p)->GetObjectInPath(a,b,c,d)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-#endif // DIRECTSOUND_VERSION >= 0x0800
-
-//
-// IDirectSound3DListener
-//
-
-DEFINE_GUID(IID_IDirectSound3DListener, 0x279AFA84, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60);
-
-#undef INTERFACE
-#define INTERFACE IDirectSound3DListener
-
-DECLARE_INTERFACE_(IDirectSound3DListener, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSound3DListener methods
- STDMETHOD(GetAllParameters) (THIS_ LPDS3DLISTENER pListener) PURE;
- STDMETHOD(GetDistanceFactor) (THIS_ D3DVALUE* pflDistanceFactor) PURE;
- STDMETHOD(GetDopplerFactor) (THIS_ D3DVALUE* pflDopplerFactor) PURE;
- STDMETHOD(GetOrientation) (THIS_ D3DVECTOR* pvOrientFront, D3DVECTOR* pvOrientTop) PURE;
- STDMETHOD(GetPosition) (THIS_ D3DVECTOR* pvPosition) PURE;
- STDMETHOD(GetRolloffFactor) (THIS_ D3DVALUE* pflRolloffFactor) PURE;
- STDMETHOD(GetVelocity) (THIS_ D3DVECTOR* pvVelocity) PURE;
- STDMETHOD(SetAllParameters) (THIS_ LPCDS3DLISTENER pcListener, DWORD dwApply) PURE;
- STDMETHOD(SetDistanceFactor) (THIS_ D3DVALUE flDistanceFactor, DWORD dwApply) PURE;
- STDMETHOD(SetDopplerFactor) (THIS_ D3DVALUE flDopplerFactor, DWORD dwApply) PURE;
- STDMETHOD(SetOrientation) (THIS_ D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront,
- D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop, DWORD dwApply) PURE;
- STDMETHOD(SetPosition) (THIS_ D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD dwApply) PURE;
- STDMETHOD(SetRolloffFactor) (THIS_ D3DVALUE flRolloffFactor, DWORD dwApply) PURE;
- STDMETHOD(SetVelocity) (THIS_ D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD dwApply) PURE;
- STDMETHOD(CommitDeferredSettings) (THIS) PURE;
-};
-
-#define IDirectSound3DListener_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSound3DListener_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSound3DListener_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSound3DListener_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a)
-#define IDirectSound3DListener_GetDistanceFactor(p,a) (p)->lpVtbl->GetDistanceFactor(p,a)
-#define IDirectSound3DListener_GetDopplerFactor(p,a) (p)->lpVtbl->GetDopplerFactor(p,a)
-#define IDirectSound3DListener_GetOrientation(p,a,b) (p)->lpVtbl->GetOrientation(p,a,b)
-#define IDirectSound3DListener_GetPosition(p,a) (p)->lpVtbl->GetPosition(p,a)
-#define IDirectSound3DListener_GetRolloffFactor(p,a) (p)->lpVtbl->GetRolloffFactor(p,a)
-#define IDirectSound3DListener_GetVelocity(p,a) (p)->lpVtbl->GetVelocity(p,a)
-#define IDirectSound3DListener_SetAllParameters(p,a,b) (p)->lpVtbl->SetAllParameters(p,a,b)
-#define IDirectSound3DListener_SetDistanceFactor(p,a,b) (p)->lpVtbl->SetDistanceFactor(p,a,b)
-#define IDirectSound3DListener_SetDopplerFactor(p,a,b) (p)->lpVtbl->SetDopplerFactor(p,a,b)
-#define IDirectSound3DListener_SetOrientation(p,a,b,c,d,e,f,g) (p)->lpVtbl->SetOrientation(p,a,b,c,d,e,f,g)
-#define IDirectSound3DListener_SetPosition(p,a,b,c,d) (p)->lpVtbl->SetPosition(p,a,b,c,d)
-#define IDirectSound3DListener_SetRolloffFactor(p,a,b) (p)->lpVtbl->SetRolloffFactor(p,a,b)
-#define IDirectSound3DListener_SetVelocity(p,a,b,c,d) (p)->lpVtbl->SetVelocity(p,a,b,c,d)
-#define IDirectSound3DListener_CommitDeferredSettings(p) (p)->lpVtbl->CommitDeferredSettings(p)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSound3DListener_GetAllParameters(p,a) (p)->GetAllParameters(a)
-#define IDirectSound3DListener_GetDistanceFactor(p,a) (p)->GetDistanceFactor(a)
-#define IDirectSound3DListener_GetDopplerFactor(p,a) (p)->GetDopplerFactor(a)
-#define IDirectSound3DListener_GetOrientation(p,a,b) (p)->GetOrientation(a,b)
-#define IDirectSound3DListener_GetPosition(p,a) (p)->GetPosition(a)
-#define IDirectSound3DListener_GetRolloffFactor(p,a) (p)->GetRolloffFactor(a)
-#define IDirectSound3DListener_GetVelocity(p,a) (p)->GetVelocity(a)
-#define IDirectSound3DListener_SetAllParameters(p,a,b) (p)->SetAllParameters(a,b)
-#define IDirectSound3DListener_SetDistanceFactor(p,a,b) (p)->SetDistanceFactor(a,b)
-#define IDirectSound3DListener_SetDopplerFactor(p,a,b) (p)->SetDopplerFactor(a,b)
-#define IDirectSound3DListener_SetOrientation(p,a,b,c,d,e,f,g) (p)->SetOrientation(a,b,c,d,e,f,g)
-#define IDirectSound3DListener_SetPosition(p,a,b,c,d) (p)->SetPosition(a,b,c,d)
-#define IDirectSound3DListener_SetRolloffFactor(p,a,b) (p)->SetRolloffFactor(a,b)
-#define IDirectSound3DListener_SetVelocity(p,a,b,c,d) (p)->SetVelocity(a,b,c,d)
-#define IDirectSound3DListener_CommitDeferredSettings(p) (p)->CommitDeferredSettings()
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-//
-// IDirectSound3DBuffer
-//
-
-DEFINE_GUID(IID_IDirectSound3DBuffer, 0x279AFA86, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60);
-
-#undef INTERFACE
-#define INTERFACE IDirectSound3DBuffer
-
-DECLARE_INTERFACE_(IDirectSound3DBuffer, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSound3DBuffer methods
- STDMETHOD(GetAllParameters) (THIS_ LPDS3DBUFFER pDs3dBuffer) PURE;
- STDMETHOD(GetConeAngles) (THIS_ LPDWORD pdwInsideConeAngle, LPDWORD pdwOutsideConeAngle) PURE;
- STDMETHOD(GetConeOrientation) (THIS_ D3DVECTOR* pvOrientation) PURE;
- STDMETHOD(GetConeOutsideVolume) (THIS_ LPLONG plConeOutsideVolume) PURE;
- STDMETHOD(GetMaxDistance) (THIS_ D3DVALUE* pflMaxDistance) PURE;
- STDMETHOD(GetMinDistance) (THIS_ D3DVALUE* pflMinDistance) PURE;
- STDMETHOD(GetMode) (THIS_ LPDWORD pdwMode) PURE;
- STDMETHOD(GetPosition) (THIS_ D3DVECTOR* pvPosition) PURE;
- STDMETHOD(GetVelocity) (THIS_ D3DVECTOR* pvVelocity) PURE;
- STDMETHOD(SetAllParameters) (THIS_ LPCDS3DBUFFER pcDs3dBuffer, DWORD dwApply) PURE;
- STDMETHOD(SetConeAngles) (THIS_ DWORD dwInsideConeAngle, DWORD dwOutsideConeAngle, DWORD dwApply) PURE;
- STDMETHOD(SetConeOrientation) (THIS_ D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD dwApply) PURE;
- STDMETHOD(SetConeOutsideVolume) (THIS_ LONG lConeOutsideVolume, DWORD dwApply) PURE;
- STDMETHOD(SetMaxDistance) (THIS_ D3DVALUE flMaxDistance, DWORD dwApply) PURE;
- STDMETHOD(SetMinDistance) (THIS_ D3DVALUE flMinDistance, DWORD dwApply) PURE;
- STDMETHOD(SetMode) (THIS_ DWORD dwMode, DWORD dwApply) PURE;
- STDMETHOD(SetPosition) (THIS_ D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD dwApply) PURE;
- STDMETHOD(SetVelocity) (THIS_ D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD dwApply) PURE;
-};
-
-#define IDirectSound3DBuffer_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSound3DBuffer_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSound3DBuffer_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSound3DBuffer_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a)
-#define IDirectSound3DBuffer_GetConeAngles(p,a,b) (p)->lpVtbl->GetConeAngles(p,a,b)
-#define IDirectSound3DBuffer_GetConeOrientation(p,a) (p)->lpVtbl->GetConeOrientation(p,a)
-#define IDirectSound3DBuffer_GetConeOutsideVolume(p,a) (p)->lpVtbl->GetConeOutsideVolume(p,a)
-#define IDirectSound3DBuffer_GetPosition(p,a) (p)->lpVtbl->GetPosition(p,a)
-#define IDirectSound3DBuffer_GetMinDistance(p,a) (p)->lpVtbl->GetMinDistance(p,a)
-#define IDirectSound3DBuffer_GetMaxDistance(p,a) (p)->lpVtbl->GetMaxDistance(p,a)
-#define IDirectSound3DBuffer_GetMode(p,a) (p)->lpVtbl->GetMode(p,a)
-#define IDirectSound3DBuffer_GetVelocity(p,a) (p)->lpVtbl->GetVelocity(p,a)
-#define IDirectSound3DBuffer_SetAllParameters(p,a,b) (p)->lpVtbl->SetAllParameters(p,a,b)
-#define IDirectSound3DBuffer_SetConeAngles(p,a,b,c) (p)->lpVtbl->SetConeAngles(p,a,b,c)
-#define IDirectSound3DBuffer_SetConeOrientation(p,a,b,c,d) (p)->lpVtbl->SetConeOrientation(p,a,b,c,d)
-#define IDirectSound3DBuffer_SetConeOutsideVolume(p,a,b) (p)->lpVtbl->SetConeOutsideVolume(p,a,b)
-#define IDirectSound3DBuffer_SetPosition(p,a,b,c,d) (p)->lpVtbl->SetPosition(p,a,b,c,d)
-#define IDirectSound3DBuffer_SetMinDistance(p,a,b) (p)->lpVtbl->SetMinDistance(p,a,b)
-#define IDirectSound3DBuffer_SetMaxDistance(p,a,b) (p)->lpVtbl->SetMaxDistance(p,a,b)
-#define IDirectSound3DBuffer_SetMode(p,a,b) (p)->lpVtbl->SetMode(p,a,b)
-#define IDirectSound3DBuffer_SetVelocity(p,a,b,c,d) (p)->lpVtbl->SetVelocity(p,a,b,c,d)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSound3DBuffer_GetAllParameters(p,a) (p)->GetAllParameters(a)
-#define IDirectSound3DBuffer_GetConeAngles(p,a,b) (p)->GetConeAngles(a,b)
-#define IDirectSound3DBuffer_GetConeOrientation(p,a) (p)->GetConeOrientation(a)
-#define IDirectSound3DBuffer_GetConeOutsideVolume(p,a) (p)->GetConeOutsideVolume(a)
-#define IDirectSound3DBuffer_GetPosition(p,a) (p)->GetPosition(a)
-#define IDirectSound3DBuffer_GetMinDistance(p,a) (p)->GetMinDistance(a)
-#define IDirectSound3DBuffer_GetMaxDistance(p,a) (p)->GetMaxDistance(a)
-#define IDirectSound3DBuffer_GetMode(p,a) (p)->GetMode(a)
-#define IDirectSound3DBuffer_GetVelocity(p,a) (p)->GetVelocity(a)
-#define IDirectSound3DBuffer_SetAllParameters(p,a,b) (p)->SetAllParameters(a,b)
-#define IDirectSound3DBuffer_SetConeAngles(p,a,b,c) (p)->SetConeAngles(a,b,c)
-#define IDirectSound3DBuffer_SetConeOrientation(p,a,b,c,d) (p)->SetConeOrientation(a,b,c,d)
-#define IDirectSound3DBuffer_SetConeOutsideVolume(p,a,b) (p)->SetConeOutsideVolume(a,b)
-#define IDirectSound3DBuffer_SetPosition(p,a,b,c,d) (p)->SetPosition(a,b,c,d)
-#define IDirectSound3DBuffer_SetMinDistance(p,a,b) (p)->SetMinDistance(a,b)
-#define IDirectSound3DBuffer_SetMaxDistance(p,a,b) (p)->SetMaxDistance(a,b)
-#define IDirectSound3DBuffer_SetMode(p,a,b) (p)->SetMode(a,b)
-#define IDirectSound3DBuffer_SetVelocity(p,a,b,c,d) (p)->SetVelocity(a,b,c,d)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-//
-// IDirectSoundCapture
-//
-
-DEFINE_GUID(IID_IDirectSoundCapture, 0xb0210781, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundCapture
-
-DECLARE_INTERFACE_(IDirectSoundCapture, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundCapture methods
- STDMETHOD(CreateCaptureBuffer) (THIS_ LPCDSCBUFFERDESC pcDSCBufferDesc, LPDIRECTSOUNDCAPTUREBUFFER *ppDSCBuffer, LPUNKNOWN pUnkOuter) PURE;
- STDMETHOD(GetCaps) (THIS_ LPDSCCAPS pDSCCaps) PURE;
- STDMETHOD(Initialize) (THIS_ LPCGUID pcGuidDevice) PURE;
-};
-
-#define IDirectSoundCapture_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundCapture_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundCapture_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundCapture_CreateCaptureBuffer(p,a,b,c) (p)->lpVtbl->CreateCaptureBuffer(p,a,b,c)
-#define IDirectSoundCapture_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a)
-#define IDirectSoundCapture_Initialize(p,a) (p)->lpVtbl->Initialize(p,a)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundCapture_CreateCaptureBuffer(p,a,b,c) (p)->CreateCaptureBuffer(a,b,c)
-#define IDirectSoundCapture_GetCaps(p,a) (p)->GetCaps(a)
-#define IDirectSoundCapture_Initialize(p,a) (p)->Initialize(a)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-//
-// IDirectSoundCaptureBuffer
-//
-
-DEFINE_GUID(IID_IDirectSoundCaptureBuffer, 0xb0210782, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundCaptureBuffer
-
-DECLARE_INTERFACE_(IDirectSoundCaptureBuffer, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundCaptureBuffer methods
- STDMETHOD(GetCaps) (THIS_ LPDSCBCAPS pDSCBCaps) PURE;
- STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD pdwCapturePosition, LPDWORD pdwReadPosition) PURE;
- STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX pwfxFormat, DWORD dwSizeAllocated, LPDWORD pdwSizeWritten) PURE;
- STDMETHOD(GetStatus) (THIS_ LPDWORD pdwStatus) PURE;
- STDMETHOD(Initialize) (THIS_ LPDIRECTSOUNDCAPTURE pDirectSoundCapture, LPCDSCBUFFERDESC pcDSCBufferDesc) PURE;
- STDMETHOD(Lock) (THIS_ DWORD dwOffset, DWORD dwBytes, LPVOID *ppvAudioPtr1, LPDWORD pdwAudioBytes1,
- LPVOID *ppvAudioPtr2, LPDWORD pdwAudioBytes2, DWORD dwFlags) PURE;
- STDMETHOD(Start) (THIS_ DWORD dwFlags) PURE;
- STDMETHOD(Stop) (THIS) PURE;
- STDMETHOD(Unlock) (THIS_ LPVOID pvAudioPtr1, DWORD dwAudioBytes1, LPVOID pvAudioPtr2, DWORD dwAudioBytes2) PURE;
-};
-
-#define IDirectSoundCaptureBuffer_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundCaptureBuffer_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundCaptureBuffer_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundCaptureBuffer_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a)
-#define IDirectSoundCaptureBuffer_GetCurrentPosition(p,a,b) (p)->lpVtbl->GetCurrentPosition(p,a,b)
-#define IDirectSoundCaptureBuffer_GetFormat(p,a,b,c) (p)->lpVtbl->GetFormat(p,a,b,c)
-#define IDirectSoundCaptureBuffer_GetStatus(p,a) (p)->lpVtbl->GetStatus(p,a)
-#define IDirectSoundCaptureBuffer_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b)
-#define IDirectSoundCaptureBuffer_Lock(p,a,b,c,d,e,f,g) (p)->lpVtbl->Lock(p,a,b,c,d,e,f,g)
-#define IDirectSoundCaptureBuffer_Start(p,a) (p)->lpVtbl->Start(p,a)
-#define IDirectSoundCaptureBuffer_Stop(p) (p)->lpVtbl->Stop(p)
-#define IDirectSoundCaptureBuffer_Unlock(p,a,b,c,d) (p)->lpVtbl->Unlock(p,a,b,c,d)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundCaptureBuffer_GetCaps(p,a) (p)->GetCaps(a)
-#define IDirectSoundCaptureBuffer_GetCurrentPosition(p,a,b) (p)->GetCurrentPosition(a,b)
-#define IDirectSoundCaptureBuffer_GetFormat(p,a,b,c) (p)->GetFormat(a,b,c)
-#define IDirectSoundCaptureBuffer_GetStatus(p,a) (p)->GetStatus(a)
-#define IDirectSoundCaptureBuffer_Initialize(p,a,b) (p)->Initialize(a,b)
-#define IDirectSoundCaptureBuffer_Lock(p,a,b,c,d,e,f,g) (p)->Lock(a,b,c,d,e,f,g)
-#define IDirectSoundCaptureBuffer_Start(p,a) (p)->Start(a)
-#define IDirectSoundCaptureBuffer_Stop(p) (p)->Stop()
-#define IDirectSoundCaptureBuffer_Unlock(p,a,b,c,d) (p)->Unlock(a,b,c,d)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-
-#if DIRECTSOUND_VERSION >= 0x0800
-
-//
-// IDirectSoundCaptureBuffer8
-//
-
-DEFINE_GUID(IID_IDirectSoundCaptureBuffer8, 0x990df4, 0xdbb, 0x4872, 0x83, 0x3e, 0x6d, 0x30, 0x3e, 0x80, 0xae, 0xb6);
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundCaptureBuffer8
-
-DECLARE_INTERFACE_(IDirectSoundCaptureBuffer8, IDirectSoundCaptureBuffer)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundCaptureBuffer methods
- STDMETHOD(GetCaps) (THIS_ LPDSCBCAPS pDSCBCaps) PURE;
- STDMETHOD(GetCurrentPosition) (THIS_ LPDWORD pdwCapturePosition, LPDWORD pdwReadPosition) PURE;
- STDMETHOD(GetFormat) (THIS_ LPWAVEFORMATEX pwfxFormat, DWORD dwSizeAllocated, LPDWORD pdwSizeWritten) PURE;
- STDMETHOD(GetStatus) (THIS_ LPDWORD pdwStatus) PURE;
- STDMETHOD(Initialize) (THIS_ LPDIRECTSOUNDCAPTURE pDirectSoundCapture, LPCDSCBUFFERDESC pcDSCBufferDesc) PURE;
- STDMETHOD(Lock) (THIS_ DWORD dwOffset, DWORD dwBytes, LPVOID *ppvAudioPtr1, LPDWORD pdwAudioBytes1,
- LPVOID *ppvAudioPtr2, LPDWORD pdwAudioBytes2, DWORD dwFlags) PURE;
- STDMETHOD(Start) (THIS_ DWORD dwFlags) PURE;
- STDMETHOD(Stop) (THIS) PURE;
- STDMETHOD(Unlock) (THIS_ LPVOID pvAudioPtr1, DWORD dwAudioBytes1, LPVOID pvAudioPtr2, DWORD dwAudioBytes2) PURE;
-
- // IDirectSoundCaptureBuffer8 methods
- STDMETHOD(GetObjectInPath) (THIS_ REFGUID rguidObject, DWORD dwIndex, REFGUID rguidInterface, LPVOID *ppObject) PURE;
- STDMETHOD(GetFXStatus) (DWORD dwFXCount, LPDWORD pdwFXStatus) PURE;
-};
-
-#define IDirectSoundCaptureBuffer8_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundCaptureBuffer8_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundCaptureBuffer8_Release(p) IUnknown_Release(p)
-
-#define IDirectSoundCaptureBuffer8_GetCaps(p,a) IDirectSoundCaptureBuffer_GetCaps(p,a)
-#define IDirectSoundCaptureBuffer8_GetCurrentPosition(p,a,b) IDirectSoundCaptureBuffer_GetCurrentPosition(p,a,b)
-#define IDirectSoundCaptureBuffer8_GetFormat(p,a,b,c) IDirectSoundCaptureBuffer_GetFormat(p,a,b,c)
-#define IDirectSoundCaptureBuffer8_GetStatus(p,a) IDirectSoundCaptureBuffer_GetStatus(p,a)
-#define IDirectSoundCaptureBuffer8_Initialize(p,a,b) IDirectSoundCaptureBuffer_Initialize(p,a,b)
-#define IDirectSoundCaptureBuffer8_Lock(p,a,b,c,d,e,f,g) IDirectSoundCaptureBuffer_Lock(p,a,b,c,d,e,f,g)
-#define IDirectSoundCaptureBuffer8_Start(p,a) IDirectSoundCaptureBuffer_Start(p,a)
-#define IDirectSoundCaptureBuffer8_Stop(p) IDirectSoundCaptureBuffer_Stop(p))
-#define IDirectSoundCaptureBuffer8_Unlock(p,a,b,c,d) IDirectSoundCaptureBuffer_Unlock(p,a,b,c,d)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundCaptureBuffer8_GetObjectInPath(p,a,b,c,d) (p)->lpVtbl->GetObjectInPath(p,a,b,c,d)
-#define IDirectSoundCaptureBuffer8_GetFXStatus(p,a,b) (p)->lpVtbl->GetFXStatus(p,a,b)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundCaptureBuffer8_GetObjectInPath(p,a,b,c,d) (p)->GetObjectInPath(a,b,c,d)
-#define IDirectSoundCaptureBuffer8_GetFXStatus(p,a,b) (p)->GetFXStatus(a,b)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-#endif // DIRECTSOUND_VERSION >= 0x0800
-
-//
-// IDirectSoundNotify
-//
-
-DEFINE_GUID(IID_IDirectSoundNotify, 0xb0210783, 0x89cd, 0x11d0, 0xaf, 0x8, 0x0, 0xa0, 0xc9, 0x25, 0xcd, 0x16);
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundNotify
-
-DECLARE_INTERFACE_(IDirectSoundNotify, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundNotify methods
- STDMETHOD(SetNotificationPositions) (THIS_ DWORD dwPositionNotifies, LPCDSBPOSITIONNOTIFY pcPositionNotifies) PURE;
-};
-
-#define IDirectSoundNotify_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundNotify_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundNotify_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundNotify_SetNotificationPositions(p,a,b) (p)->lpVtbl->SetNotificationPositions(p,a,b)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundNotify_SetNotificationPositions(p,a,b) (p)->SetNotificationPositions(a,b)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-//
-// IKsPropertySet
-//
-
-#ifndef _IKsPropertySet_
-#define _IKsPropertySet_
-
-#ifdef __cplusplus
-// 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined
-struct IKsPropertySet;
-#endif // __cplusplus
-
-typedef struct IKsPropertySet *LPKSPROPERTYSET;
-
-#define KSPROPERTY_SUPPORT_GET 0x00000001
-#define KSPROPERTY_SUPPORT_SET 0x00000002
-
-DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93);
-
-#undef INTERFACE
-#define INTERFACE IKsPropertySet
-
-DECLARE_INTERFACE_(IKsPropertySet, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IKsPropertySet methods
- STDMETHOD(Get) (THIS_ REFGUID rguidPropSet, ULONG ulId, LPVOID pInstanceData, ULONG ulInstanceLength,
- LPVOID pPropertyData, ULONG ulDataLength, PULONG pulBytesReturned) PURE;
- STDMETHOD(Set) (THIS_ REFGUID rguidPropSet, ULONG ulId, LPVOID pInstanceData, ULONG ulInstanceLength,
- LPVOID pPropertyData, ULONG ulDataLength) PURE;
- STDMETHOD(QuerySupport) (THIS_ REFGUID rguidPropSet, ULONG ulId, PULONG pulTypeSupport) PURE;
-};
-
-#define IKsPropertySet_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IKsPropertySet_AddRef(p) IUnknown_AddRef(p)
-#define IKsPropertySet_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IKsPropertySet_Get(p,a,b,c,d,e,f,g) (p)->lpVtbl->Get(p,a,b,c,d,e,f,g)
-#define IKsPropertySet_Set(p,a,b,c,d,e,f) (p)->lpVtbl->Set(p,a,b,c,d,e,f)
-#define IKsPropertySet_QuerySupport(p,a,b,c) (p)->lpVtbl->QuerySupport(p,a,b,c)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IKsPropertySet_Get(p,a,b,c,d,e,f,g) (p)->Get(a,b,c,d,e,f,g)
-#define IKsPropertySet_Set(p,a,b,c,d,e,f) (p)->Set(a,b,c,d,e,f)
-#define IKsPropertySet_QuerySupport(p,a,b,c) (p)->QuerySupport(a,b,c)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-#endif // _IKsPropertySet_
-
-#if DIRECTSOUND_VERSION >= 0x0800
-
-//
-// IDirectSoundFXGargle
-//
-
-DEFINE_GUID(IID_IDirectSoundFXGargle, 0xd616f352, 0xd622, 0x11ce, 0xaa, 0xc5, 0x00, 0x20, 0xaf, 0x0b, 0x99, 0xa3);
-
-typedef struct _DSFXGargle
-{
- DWORD dwRateHz; // Rate of modulation in hz
- DWORD dwWaveShape; // DSFXGARGLE_WAVE_xxx
-} DSFXGargle, *LPDSFXGargle;
-
-#define DSFXGARGLE_WAVE_TRIANGLE 0
-#define DSFXGARGLE_WAVE_SQUARE 1
-
-typedef const DSFXGargle *LPCDSFXGargle;
-
-#define DSFXGARGLE_RATEHZ_MIN 1
-#define DSFXGARGLE_RATEHZ_MAX 1000
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundFXGargle
-
-DECLARE_INTERFACE_(IDirectSoundFXGargle, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundFXGargle methods
- STDMETHOD(SetAllParameters) (THIS_ LPCDSFXGargle pcDsFxGargle) PURE;
- STDMETHOD(GetAllParameters) (THIS_ LPDSFXGargle pDsFxGargle) PURE;
-};
-
-#define IDirectSoundFXGargle_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundFXGargle_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundFXGargle_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXGargle_SetAllParameters(p,a) (p)->lpVtbl->SetAllParameters(p,a)
-#define IDirectSoundFXGargle_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXGargle_SetAllParameters(p,a) (p)->SetAllParameters(a)
-#define IDirectSoundFXGargle_GetAllParameters(p,a) (p)->GetAllParameters(a)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-//
-// IDirectSoundFXChorus
-//
-
-DEFINE_GUID(IID_IDirectSoundFXChorus, 0x880842e3, 0x145f, 0x43e6, 0xa9, 0x34, 0xa7, 0x18, 0x06, 0xe5, 0x05, 0x47);
-
-typedef struct _DSFXChorus
-{
- FLOAT fWetDryMix;
- FLOAT fDepth;
- FLOAT fFeedback;
- FLOAT fFrequency;
- LONG lWaveform; // LFO shape; DSFXCHORUS_WAVE_xxx
- FLOAT fDelay;
- LONG lPhase;
-} DSFXChorus, *LPDSFXChorus;
-
-typedef const DSFXChorus *LPCDSFXChorus;
-
-#define DSFXCHORUS_WAVE_TRIANGLE 0
-#define DSFXCHORUS_WAVE_SIN 1
-
-#define DSFXCHORUS_WETDRYMIX_MIN 0.0f
-#define DSFXCHORUS_WETDRYMIX_MAX 100.0f
-#define DSFXCHORUS_DEPTH_MIN 0.0f
-#define DSFXCHORUS_DEPTH_MAX 100.0f
-#define DSFXCHORUS_FEEDBACK_MIN -99.0f
-#define DSFXCHORUS_FEEDBACK_MAX 99.0f
-#define DSFXCHORUS_FREQUENCY_MIN 0.0f
-#define DSFXCHORUS_FREQUENCY_MAX 10.0f
-#define DSFXCHORUS_DELAY_MIN 0.0f
-#define DSFXCHORUS_DELAY_MAX 20.0f
-#define DSFXCHORUS_PHASE_MIN 0
-#define DSFXCHORUS_PHASE_MAX 4
-
-#define DSFXCHORUS_PHASE_NEG_180 0
-#define DSFXCHORUS_PHASE_NEG_90 1
-#define DSFXCHORUS_PHASE_ZERO 2
-#define DSFXCHORUS_PHASE_90 3
-#define DSFXCHORUS_PHASE_180 4
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundFXChorus
-
-DECLARE_INTERFACE_(IDirectSoundFXChorus, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundFXChorus methods
- STDMETHOD(SetAllParameters) (THIS_ LPCDSFXChorus pcDsFxChorus) PURE;
- STDMETHOD(GetAllParameters) (THIS_ LPDSFXChorus pDsFxChorus) PURE;
-};
-
-#define IDirectSoundFXChorus_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundFXChorus_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundFXChorus_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXChorus_SetAllParameters(p,a) (p)->lpVtbl->SetAllParameters(p,a)
-#define IDirectSoundFXChorus_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXChorus_SetAllParameters(p,a) (p)->SetAllParameters(a)
-#define IDirectSoundFXChorus_GetAllParameters(p,a) (p)->GetAllParameters(a)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-//
-// IDirectSoundFXFlanger
-//
-
-DEFINE_GUID(IID_IDirectSoundFXFlanger, 0x903e9878, 0x2c92, 0x4072, 0x9b, 0x2c, 0xea, 0x68, 0xf5, 0x39, 0x67, 0x83);
-
-typedef struct _DSFXFlanger
-{
- FLOAT fWetDryMix;
- FLOAT fDepth;
- FLOAT fFeedback;
- FLOAT fFrequency;
- LONG lWaveform;
- FLOAT fDelay;
- LONG lPhase;
-} DSFXFlanger, *LPDSFXFlanger;
-
-typedef const DSFXFlanger *LPCDSFXFlanger;
-
-#define DSFXFLANGER_WAVE_TRIANGLE 0
-#define DSFXFLANGER_WAVE_SIN 1
-
-#define DSFXFLANGER_WETDRYMIX_MIN 0.0f
-#define DSFXFLANGER_WETDRYMIX_MAX 100.0f
-#define DSFXFLANGER_FREQUENCY_MIN 0.0f
-#define DSFXFLANGER_FREQUENCY_MAX 10.0f
-#define DSFXFLANGER_DEPTH_MIN 0.0f
-#define DSFXFLANGER_DEPTH_MAX 100.0f
-#define DSFXFLANGER_PHASE_MIN 0
-#define DSFXFLANGER_PHASE_MAX 4
-#define DSFXFLANGER_FEEDBACK_MIN -99.0f
-#define DSFXFLANGER_FEEDBACK_MAX 99.0f
-#define DSFXFLANGER_DELAY_MIN 0.0f
-#define DSFXFLANGER_DELAY_MAX 4.0f
-
-#define DSFXFLANGER_PHASE_NEG_180 0
-#define DSFXFLANGER_PHASE_NEG_90 1
-#define DSFXFLANGER_PHASE_ZERO 2
-#define DSFXFLANGER_PHASE_90 3
-#define DSFXFLANGER_PHASE_180 4
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundFXFlanger
-
-DECLARE_INTERFACE_(IDirectSoundFXFlanger, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundFXFlanger methods
- STDMETHOD(SetAllParameters) (THIS_ LPCDSFXFlanger pcDsFxFlanger) PURE;
- STDMETHOD(GetAllParameters) (THIS_ LPDSFXFlanger pDsFxFlanger) PURE;
-};
-
-#define IDirectSoundFXFlanger_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundFXFlanger_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundFXFlanger_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXFlanger_SetAllParameters(p,a) (p)->lpVtbl->SetAllParameters(p,a)
-#define IDirectSoundFXFlanger_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXFlanger_SetAllParameters(p,a) (p)->SetAllParameters(a)
-#define IDirectSoundFXFlanger_GetAllParameters(p,a) (p)->GetAllParameters(a)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-//
-// IDirectSoundFXEcho
-//
-
-DEFINE_GUID(IID_IDirectSoundFXEcho, 0x8bd28edf, 0x50db, 0x4e92, 0xa2, 0xbd, 0x44, 0x54, 0x88, 0xd1, 0xed, 0x42);
-
-typedef struct _DSFXEcho
-{
- FLOAT fWetDryMix;
- FLOAT fFeedback;
- FLOAT fLeftDelay;
- FLOAT fRightDelay;
- LONG lPanDelay;
-} DSFXEcho, *LPDSFXEcho;
-
-typedef const DSFXEcho *LPCDSFXEcho;
-
-#define DSFXECHO_WETDRYMIX_MIN 0.0f
-#define DSFXECHO_WETDRYMIX_MAX 100.0f
-#define DSFXECHO_FEEDBACK_MIN 0.0f
-#define DSFXECHO_FEEDBACK_MAX 100.0f
-#define DSFXECHO_LEFTDELAY_MIN 1.0f
-#define DSFXECHO_LEFTDELAY_MAX 2000.0f
-#define DSFXECHO_RIGHTDELAY_MIN 1.0f
-#define DSFXECHO_RIGHTDELAY_MAX 2000.0f
-#define DSFXECHO_PANDELAY_MIN 0
-#define DSFXECHO_PANDELAY_MAX 1
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundFXEcho
-
-DECLARE_INTERFACE_(IDirectSoundFXEcho, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundFXEcho methods
- STDMETHOD(SetAllParameters) (THIS_ LPCDSFXEcho pcDsFxEcho) PURE;
- STDMETHOD(GetAllParameters) (THIS_ LPDSFXEcho pDsFxEcho) PURE;
-};
-
-#define IDirectSoundFXEcho_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundFXEcho_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundFXEcho_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXEcho_SetAllParameters(p,a) (p)->lpVtbl->SetAllParameters(p,a)
-#define IDirectSoundFXEcho_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXEcho_SetAllParameters(p,a) (p)->SetAllParameters(a)
-#define IDirectSoundFXEcho_GetAllParameters(p,a) (p)->GetAllParameters(a)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-//
-// IDirectSoundFXDistortion
-//
-
-DEFINE_GUID(IID_IDirectSoundFXDistortion, 0x8ecf4326, 0x455f, 0x4d8b, 0xbd, 0xa9, 0x8d, 0x5d, 0x3e, 0x9e, 0x3e, 0x0b);
-
-typedef struct _DSFXDistortion
-{
- FLOAT fGain;
- FLOAT fEdge;
- FLOAT fPostEQCenterFrequency;
- FLOAT fPostEQBandwidth;
- FLOAT fPreLowpassCutoff;
-} DSFXDistortion, *LPDSFXDistortion;
-
-typedef const DSFXDistortion *LPCDSFXDistortion;
-
-#define DSFXDISTORTION_GAIN_MIN -60.0f
-#define DSFXDISTORTION_GAIN_MAX 0.0f
-#define DSFXDISTORTION_EDGE_MIN 0.0f
-#define DSFXDISTORTION_EDGE_MAX 100.0f
-#define DSFXDISTORTION_POSTEQCENTERFREQUENCY_MIN 100.0f
-#define DSFXDISTORTION_POSTEQCENTERFREQUENCY_MAX 8000.0f
-#define DSFXDISTORTION_POSTEQBANDWIDTH_MIN 100.0f
-#define DSFXDISTORTION_POSTEQBANDWIDTH_MAX 8000.0f
-#define DSFXDISTORTION_PRELOWPASSCUTOFF_MIN 100.0f
-#define DSFXDISTORTION_PRELOWPASSCUTOFF_MAX 8000.0f
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundFXDistortion
-
-DECLARE_INTERFACE_(IDirectSoundFXDistortion, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundFXDistortion methods
- STDMETHOD(SetAllParameters) (THIS_ LPCDSFXDistortion pcDsFxDistortion) PURE;
- STDMETHOD(GetAllParameters) (THIS_ LPDSFXDistortion pDsFxDistortion) PURE;
-};
-
-#define IDirectSoundFXDistortion_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundFXDistortion_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundFXDistortion_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXDistortion_SetAllParameters(p,a) (p)->lpVtbl->SetAllParameters(p,a)
-#define IDirectSoundFXDistortion_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXDistortion_SetAllParameters(p,a) (p)->SetAllParameters(a)
-#define IDirectSoundFXDistortion_GetAllParameters(p,a) (p)->GetAllParameters(a)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-//
-// IDirectSoundFXCompressor
-//
-
-DEFINE_GUID(IID_IDirectSoundFXCompressor, 0x4bbd1154, 0x62f6, 0x4e2c, 0xa1, 0x5c, 0xd3, 0xb6, 0xc4, 0x17, 0xf7, 0xa0);
-
-typedef struct _DSFXCompressor
-{
- FLOAT fGain;
- FLOAT fAttack;
- FLOAT fRelease;
- FLOAT fThreshold;
- FLOAT fRatio;
- FLOAT fPredelay;
-} DSFXCompressor, *LPDSFXCompressor;
-
-typedef const DSFXCompressor *LPCDSFXCompressor;
-
-#define DSFXCOMPRESSOR_GAIN_MIN -60.0f
-#define DSFXCOMPRESSOR_GAIN_MAX 60.0f
-#define DSFXCOMPRESSOR_ATTACK_MIN 0.01f
-#define DSFXCOMPRESSOR_ATTACK_MAX 500.0f
-#define DSFXCOMPRESSOR_RELEASE_MIN 50.0f
-#define DSFXCOMPRESSOR_RELEASE_MAX 3000.0f
-#define DSFXCOMPRESSOR_THRESHOLD_MIN -60.0f
-#define DSFXCOMPRESSOR_THRESHOLD_MAX 0.0f
-#define DSFXCOMPRESSOR_RATIO_MIN 1.0f
-#define DSFXCOMPRESSOR_RATIO_MAX 100.0f
-#define DSFXCOMPRESSOR_PREDELAY_MIN 0.0f
-#define DSFXCOMPRESSOR_PREDELAY_MAX 4.0f
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundFXCompressor
-
-DECLARE_INTERFACE_(IDirectSoundFXCompressor, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundFXCompressor methods
- STDMETHOD(SetAllParameters) (THIS_ LPCDSFXCompressor pcDsFxCompressor) PURE;
- STDMETHOD(GetAllParameters) (THIS_ LPDSFXCompressor pDsFxCompressor) PURE;
-};
-
-#define IDirectSoundFXCompressor_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundFXCompressor_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundFXCompressor_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXCompressor_SetAllParameters(p,a) (p)->lpVtbl->SetAllParameters(p,a)
-#define IDirectSoundFXCompressor_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXCompressor_SetAllParameters(p,a) (p)->SetAllParameters(a)
-#define IDirectSoundFXCompressor_GetAllParameters(p,a) (p)->GetAllParameters(a)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-//
-// IDirectSoundFXParamEq
-//
-
-DEFINE_GUID(IID_IDirectSoundFXParamEq, 0xc03ca9fe, 0xfe90, 0x4204, 0x80, 0x78, 0x82, 0x33, 0x4c, 0xd1, 0x77, 0xda);
-
-typedef struct _DSFXParamEq
-{
- FLOAT fCenter;
- FLOAT fBandwidth;
- FLOAT fGain;
-} DSFXParamEq, *LPDSFXParamEq;
-
-typedef const DSFXParamEq *LPCDSFXParamEq;
-
-#define DSFXPARAMEQ_CENTER_MIN 80.0f
-#define DSFXPARAMEQ_CENTER_MAX 16000.0f
-#define DSFXPARAMEQ_BANDWIDTH_MIN 1.0f
-#define DSFXPARAMEQ_BANDWIDTH_MAX 36.0f
-#define DSFXPARAMEQ_GAIN_MIN -15.0f
-#define DSFXPARAMEQ_GAIN_MAX 15.0f
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundFXParamEq
-
-DECLARE_INTERFACE_(IDirectSoundFXParamEq, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundFXParamEq methods
- STDMETHOD(SetAllParameters) (THIS_ LPCDSFXParamEq pcDsFxParamEq) PURE;
- STDMETHOD(GetAllParameters) (THIS_ LPDSFXParamEq pDsFxParamEq) PURE;
-};
-
-#define IDirectSoundFXParamEq_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundFXParamEq_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundFXParamEq_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXParamEq_SetAllParameters(p,a) (p)->lpVtbl->SetAllParameters(p,a)
-#define IDirectSoundFXParamEq_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXParamEq_SetAllParameters(p,a) (p)->SetAllParameters(a)
-#define IDirectSoundFXParamEq_GetAllParameters(p,a) (p)->GetAllParameters(a)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-//
-// IDirectSoundFXI3DL2Reverb
-//
-
-DEFINE_GUID(IID_IDirectSoundFXI3DL2Reverb, 0x4b166a6a, 0x0d66, 0x43f3, 0x80, 0xe3, 0xee, 0x62, 0x80, 0xde, 0xe1, 0xa4);
-
-typedef struct _DSFXI3DL2Reverb
-{
- LONG lRoom; // [-10000, 0] default: -1000 mB
- LONG lRoomHF; // [-10000, 0] default: 0 mB
- FLOAT flRoomRolloffFactor; // [0.0, 10.0] default: 0.0
- FLOAT flDecayTime; // [0.1, 20.0] default: 1.49s
- FLOAT flDecayHFRatio; // [0.1, 2.0] default: 0.83
- LONG lReflections; // [-10000, 1000] default: -2602 mB
- FLOAT flReflectionsDelay; // [0.0, 0.3] default: 0.007 s
- LONG lReverb; // [-10000, 2000] default: 200 mB
- FLOAT flReverbDelay; // [0.0, 0.1] default: 0.011 s
- FLOAT flDiffusion; // [0.0, 100.0] default: 100.0 %
- FLOAT flDensity; // [0.0, 100.0] default: 100.0 %
- FLOAT flHFReference; // [20.0, 20000.0] default: 5000.0 Hz
-} DSFXI3DL2Reverb, *LPDSFXI3DL2Reverb;
-
-typedef const DSFXI3DL2Reverb *LPCDSFXI3DL2Reverb;
-
-#define DSFX_I3DL2REVERB_ROOM_MIN (-10000)
-#define DSFX_I3DL2REVERB_ROOM_MAX 0
-#define DSFX_I3DL2REVERB_ROOM_DEFAULT (-1000)
-
-#define DSFX_I3DL2REVERB_ROOMHF_MIN (-10000)
-#define DSFX_I3DL2REVERB_ROOMHF_MAX 0
-#define DSFX_I3DL2REVERB_ROOMHF_DEFAULT (-100)
-
-#define DSFX_I3DL2REVERB_ROOMROLLOFFFACTOR_MIN 0.0f
-#define DSFX_I3DL2REVERB_ROOMROLLOFFFACTOR_MAX 10.0f
-#define DSFX_I3DL2REVERB_ROOMROLLOFFFACTOR_DEFAULT 0.0f
-
-#define DSFX_I3DL2REVERB_DECAYTIME_MIN 0.1f
-#define DSFX_I3DL2REVERB_DECAYTIME_MAX 20.0f
-#define DSFX_I3DL2REVERB_DECAYTIME_DEFAULT 1.49f
-
-#define DSFX_I3DL2REVERB_DECAYHFRATIO_MIN 0.1f
-#define DSFX_I3DL2REVERB_DECAYHFRATIO_MAX 2.0f
-#define DSFX_I3DL2REVERB_DECAYHFRATIO_DEFAULT 0.83f
-
-#define DSFX_I3DL2REVERB_REFLECTIONS_MIN (-10000)
-#define DSFX_I3DL2REVERB_REFLECTIONS_MAX 1000
-#define DSFX_I3DL2REVERB_REFLECTIONS_DEFAULT (-2602)
-
-#define DSFX_I3DL2REVERB_REFLECTIONSDELAY_MIN 0.0f
-#define DSFX_I3DL2REVERB_REFLECTIONSDELAY_MAX 0.3f
-#define DSFX_I3DL2REVERB_REFLECTIONSDELAY_DEFAULT 0.007f
-
-#define DSFX_I3DL2REVERB_REVERB_MIN (-10000)
-#define DSFX_I3DL2REVERB_REVERB_MAX 2000
-#define DSFX_I3DL2REVERB_REVERB_DEFAULT (200)
-
-#define DSFX_I3DL2REVERB_REVERBDELAY_MIN 0.0f
-#define DSFX_I3DL2REVERB_REVERBDELAY_MAX 0.1f
-#define DSFX_I3DL2REVERB_REVERBDELAY_DEFAULT 0.011f
-
-#define DSFX_I3DL2REVERB_DIFFUSION_MIN 0.0f
-#define DSFX_I3DL2REVERB_DIFFUSION_MAX 100.0f
-#define DSFX_I3DL2REVERB_DIFFUSION_DEFAULT 100.0f
-
-#define DSFX_I3DL2REVERB_DENSITY_MIN 0.0f
-#define DSFX_I3DL2REVERB_DENSITY_MAX 100.0f
-#define DSFX_I3DL2REVERB_DENSITY_DEFAULT 100.0f
-
-#define DSFX_I3DL2REVERB_HFREFERENCE_MIN 20.0f
-#define DSFX_I3DL2REVERB_HFREFERENCE_MAX 20000.0f
-#define DSFX_I3DL2REVERB_HFREFERENCE_DEFAULT 5000.0f
-
-#define DSFX_I3DL2REVERB_QUALITY_MIN 0
-#define DSFX_I3DL2REVERB_QUALITY_MAX 3
-#define DSFX_I3DL2REVERB_QUALITY_DEFAULT 2
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundFXI3DL2Reverb
-
-DECLARE_INTERFACE_(IDirectSoundFXI3DL2Reverb, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundFXI3DL2Reverb methods
- STDMETHOD(SetAllParameters) (THIS_ LPCDSFXI3DL2Reverb pcDsFxI3DL2Reverb) PURE;
- STDMETHOD(GetAllParameters) (THIS_ LPDSFXI3DL2Reverb pDsFxI3DL2Reverb) PURE;
- STDMETHOD(SetPreset) (THIS_ DWORD dwPreset) PURE;
- STDMETHOD(GetPreset) (THIS_ LPDWORD pdwPreset) PURE;
- STDMETHOD(SetQuality) (THIS_ LONG lQuality) PURE;
- STDMETHOD(GetQuality) (THIS_ LONG *plQuality) PURE;
-};
-
-#define IDirectSoundFXI3DL2Reverb_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundFXI3DL2Reverb_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundFXI3DL2Reverb_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXI3DL2Reverb_SetAllParameters(p,a) (p)->lpVtbl->SetAllParameters(p,a)
-#define IDirectSoundFXI3DL2Reverb_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a)
-#define IDirectSoundFXI3DL2Reverb_SetPreset(p,a) (p)->lpVtbl->SetPreset(p,a)
-#define IDirectSoundFXI3DL2Reverb_GetPreset(p,a) (p)->lpVtbl->GetPreset(p,a)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXI3DL2Reverb_SetAllParameters(p,a) (p)->SetAllParameters(a)
-#define IDirectSoundFXI3DL2Reverb_GetAllParameters(p,a) (p)->GetAllParameters(a)
-#define IDirectSoundFXI3DL2Reverb_SetPreset(p,a) (p)->SetPreset(a)
-#define IDirectSoundFXI3DL2Reverb_GetPreset(p,a) (p)->GetPreset(a)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-//
-// IDirectSoundFXWavesReverb
-//
-
-DEFINE_GUID(IID_IDirectSoundFXWavesReverb,0x46858c3a,0x0dc6,0x45e3,0xb7,0x60,0xd4,0xee,0xf1,0x6c,0xb3,0x25);
-
-typedef struct _DSFXWavesReverb
-{
- FLOAT fInGain; // [-96.0,0.0] default: 0.0 dB
- FLOAT fReverbMix; // [-96.0,0.0] default: 0.0 db
- FLOAT fReverbTime; // [0.001,3000.0] default: 1000.0 ms
- FLOAT fHighFreqRTRatio; // [0.001,0.999] default: 0.001
-} DSFXWavesReverb, *LPDSFXWavesReverb;
-
-typedef const DSFXWavesReverb *LPCDSFXWavesReverb;
-
-#define DSFX_WAVESREVERB_INGAIN_MIN -96.0f
-#define DSFX_WAVESREVERB_INGAIN_MAX 0.0f
-#define DSFX_WAVESREVERB_INGAIN_DEFAULT 0.0f
-#define DSFX_WAVESREVERB_REVERBMIX_MIN -96.0f
-#define DSFX_WAVESREVERB_REVERBMIX_MAX 0.0f
-#define DSFX_WAVESREVERB_REVERBMIX_DEFAULT 0.0f
-#define DSFX_WAVESREVERB_REVERBTIME_MIN 0.001f
-#define DSFX_WAVESREVERB_REVERBTIME_MAX 3000.0f
-#define DSFX_WAVESREVERB_REVERBTIME_DEFAULT 1000.0f
-#define DSFX_WAVESREVERB_HIGHFREQRTRATIO_MIN 0.001f
-#define DSFX_WAVESREVERB_HIGHFREQRTRATIO_MAX 0.999f
-#define DSFX_WAVESREVERB_HIGHFREQRTRATIO_DEFAULT 0.001f
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundFXWavesReverb
-
-DECLARE_INTERFACE_(IDirectSoundFXWavesReverb, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundFXWavesReverb methods
- STDMETHOD(SetAllParameters) (THIS_ LPCDSFXWavesReverb pcDsFxWavesReverb) PURE;
- STDMETHOD(GetAllParameters) (THIS_ LPDSFXWavesReverb pDsFxWavesReverb) PURE;
-};
-
-#define IDirectSoundFXWavesReverb_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundFXWavesReverb_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundFXWavesReverb_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXWavesReverb_SetAllParameters(p,a) (p)->lpVtbl->SetAllParameters(p,a)
-#define IDirectSoundFXWavesReverb_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFXWavesReverb_SetAllParameters(p,a) (p)->SetAllParameters(a)
-#define IDirectSoundFXWavesReverb_GetAllParameters(p,a) (p)->GetAllParameters(a)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-//
-// IDirectSoundCaptureFXAec
-//
-
-DEFINE_GUID(IID_IDirectSoundCaptureFXAec, 0xad74143d, 0x903d, 0x4ab7, 0x80, 0x66, 0x28, 0xd3, 0x63, 0x03, 0x6d, 0x65);
-
-typedef struct _DSCFXAec
-{
- BOOL fEnable;
- BOOL fNoiseFill;
- DWORD dwMode;
-} DSCFXAec, *LPDSCFXAec;
-
-typedef const DSCFXAec *LPCDSCFXAec;
-
-// These match the AEC_MODE_* constants in the DDK's ksmedia.h file
-#define DSCFX_AEC_MODE_PASS_THROUGH 0x0
-#define DSCFX_AEC_MODE_HALF_DUPLEX 0x1
-#define DSCFX_AEC_MODE_FULL_DUPLEX 0x2
-
-// These match the AEC_STATUS_* constants in ksmedia.h
-#define DSCFX_AEC_STATUS_HISTORY_UNINITIALIZED 0x0
-#define DSCFX_AEC_STATUS_HISTORY_CONTINUOUSLY_CONVERGED 0x1
-#define DSCFX_AEC_STATUS_HISTORY_PREVIOUSLY_DIVERGED 0x2
-#define DSCFX_AEC_STATUS_CURRENTLY_CONVERGED 0x8
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundCaptureFXAec
-
-DECLARE_INTERFACE_(IDirectSoundCaptureFXAec, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundCaptureFXAec methods
- STDMETHOD(SetAllParameters) (THIS_ LPCDSCFXAec pDscFxAec) PURE;
- STDMETHOD(GetAllParameters) (THIS_ LPDSCFXAec pDscFxAec) PURE;
- STDMETHOD(GetStatus) (THIS_ PDWORD pdwStatus) PURE;
- STDMETHOD(Reset) (THIS) PURE;
-};
-
-#define IDirectSoundCaptureFXAec_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundCaptureFXAec_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundCaptureFXAec_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundCaptureFXAec_SetAllParameters(p,a) (p)->lpVtbl->SetAllParameters(p,a)
-#define IDirectSoundCaptureFXAec_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundCaptureFXAec_SetAllParameters(p,a) (p)->SetAllParameters(a)
-#define IDirectSoundCaptureFXAec_GetAllParameters(p,a) (p)->GetAllParameters(a)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-
-//
-// IDirectSoundCaptureFXNoiseSuppress
-//
-
-DEFINE_GUID(IID_IDirectSoundCaptureFXNoiseSuppress, 0xed311e41, 0xfbae, 0x4175, 0x96, 0x25, 0xcd, 0x8, 0x54, 0xf6, 0x93, 0xca);
-
-typedef struct _DSCFXNoiseSuppress
-{
- BOOL fEnable;
-} DSCFXNoiseSuppress, *LPDSCFXNoiseSuppress;
-
-typedef const DSCFXNoiseSuppress *LPCDSCFXNoiseSuppress;
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundCaptureFXNoiseSuppress
-
-DECLARE_INTERFACE_(IDirectSoundCaptureFXNoiseSuppress, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundCaptureFXNoiseSuppress methods
- STDMETHOD(SetAllParameters) (THIS_ LPCDSCFXNoiseSuppress pcDscFxNoiseSuppress) PURE;
- STDMETHOD(GetAllParameters) (THIS_ LPDSCFXNoiseSuppress pDscFxNoiseSuppress) PURE;
- STDMETHOD(Reset) (THIS) PURE;
-};
-
-#define IDirectSoundCaptureFXNoiseSuppress_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundCaptureFXNoiseSuppress_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundCaptureFXNoiseSuppress_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundCaptureFXNoiseSuppress_SetAllParameters(p,a) (p)->lpVtbl->SetAllParameters(p,a)
-#define IDirectSoundCaptureFXNoiseSuppress_GetAllParameters(p,a) (p)->lpVtbl->GetAllParameters(p,a)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundCaptureFXNoiseSuppress_SetAllParameters(p,a) (p)->SetAllParameters(a)
-#define IDirectSoundCaptureFXNoiseSuppress_GetAllParameters(p,a) (p)->GetAllParameters(a)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-
-//
-// IDirectSoundFullDuplex
-//
-
-#ifndef _IDirectSoundFullDuplex_
-#define _IDirectSoundFullDuplex_
-
-#ifdef __cplusplus
-// 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined
-struct IDirectSoundFullDuplex;
-#endif // __cplusplus
-
-typedef struct IDirectSoundFullDuplex *LPDIRECTSOUNDFULLDUPLEX;
-
-DEFINE_GUID(IID_IDirectSoundFullDuplex, 0xedcb4c7a, 0xdaab, 0x4216, 0xa4, 0x2e, 0x6c, 0x50, 0x59, 0x6d, 0xdc, 0x1d);
-
-#undef INTERFACE
-#define INTERFACE IDirectSoundFullDuplex
-
-DECLARE_INTERFACE_(IDirectSoundFullDuplex, IUnknown)
-{
- // IUnknown methods
- STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID *) PURE;
- STDMETHOD_(ULONG,AddRef) (THIS) PURE;
- STDMETHOD_(ULONG,Release) (THIS) PURE;
-
- // IDirectSoundFullDuplex methods
- STDMETHOD(Initialize) (THIS_ LPCGUID pCaptureGuid, LPCGUID pRenderGuid, LPCDSCBUFFERDESC lpDscBufferDesc, LPCDSBUFFERDESC lpDsBufferDesc, HWND hWnd, DWORD dwLevel, LPLPDIRECTSOUNDCAPTUREBUFFER8 lplpDirectSoundCaptureBuffer8, LPLPDIRECTSOUNDBUFFER8 lplpDirectSoundBuffer8) PURE;
-};
-
-#define IDirectSoundFullDuplex_QueryInterface(p,a,b) IUnknown_QueryInterface(p,a,b)
-#define IDirectSoundFullDuplex_AddRef(p) IUnknown_AddRef(p)
-#define IDirectSoundFullDuplex_Release(p) IUnknown_Release(p)
-
-#if !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFullDuplex_Initialize(p,a,b,c,d,e,f,g,h) (p)->lpVtbl->Initialize(p,a,b,c,d,e,f,g,h)
-#else // !defined(__cplusplus) || defined(CINTERFACE)
-#define IDirectSoundFullDuplex_Initialize(p,a,b,c,d,e,f,g,h) (p)->Initialize(a,b,c,d,e,f,g,h)
-#endif // !defined(__cplusplus) || defined(CINTERFACE)
-
-#endif // _IDirectSoundFullDuplex_
-
-#endif // DIRECTSOUND_VERSION >= 0x0800
-
-//
-// Return Codes
-//
-
-// The function completed successfully
-#define DS_OK S_OK
-
-// The call succeeded, but we had to substitute the 3D algorithm
-#define DS_NO_VIRTUALIZATION MAKE_HRESULT(0, _FACDS, 10)
-
-// The call failed because resources (such as a priority level)
-// were already being used by another caller
-#define DSERR_ALLOCATED MAKE_DSHRESULT(10)
-
-// The control (vol, pan, etc.) requested by the caller is not available
-#define DSERR_CONTROLUNAVAIL MAKE_DSHRESULT(30)
-
-// An invalid parameter was passed to the returning function
-#define DSERR_INVALIDPARAM E_INVALIDARG
-
-// This call is not valid for the current state of this object
-#define DSERR_INVALIDCALL MAKE_DSHRESULT(50)
-
-// An undetermined error occurred inside the DirectSound subsystem
-#define DSERR_GENERIC E_FAIL
-
-// The caller does not have the priority level required for the function to
-// succeed
-#define DSERR_PRIOLEVELNEEDED MAKE_DSHRESULT(70)
-
-// Not enough free memory is available to complete the operation
-#define DSERR_OUTOFMEMORY E_OUTOFMEMORY
-
-// The specified WAVE format is not supported
-#define DSERR_BADFORMAT MAKE_DSHRESULT(100)
-
-// The function called is not supported at this time
-#define DSERR_UNSUPPORTED E_NOTIMPL
-
-// No sound driver is available for use
-#define DSERR_NODRIVER MAKE_DSHRESULT(120)
-// This object is already initialized
-#define DSERR_ALREADYINITIALIZED MAKE_DSHRESULT(130)
-
-// This object does not support aggregation
-#define DSERR_NOAGGREGATION CLASS_E_NOAGGREGATION
-
-// The buffer memory has been lost, and must be restored
-#define DSERR_BUFFERLOST MAKE_DSHRESULT(150)
-
-// Another app has a higher priority level, preventing this call from
-// succeeding
-#define DSERR_OTHERAPPHASPRIO MAKE_DSHRESULT(160)
-
-// This object has not been initialized
-#define DSERR_UNINITIALIZED MAKE_DSHRESULT(170)
-
-// The requested COM interface is not available
-#define DSERR_NOINTERFACE E_NOINTERFACE
-
-// Access is denied
-#define DSERR_ACCESSDENIED E_ACCESSDENIED
-
-// Tried to create a DSBCAPS_CTRLFX buffer shorter than DSBSIZE_FX_MIN milliseconds
-#define DSERR_BUFFERTOOSMALL MAKE_DSHRESULT(180)
-
-// Attempt to use DirectSound 8 functionality on an older DirectSound object
-#define DSERR_DS8_REQUIRED MAKE_DSHRESULT(190)
-
-// A circular loop of send effects was detected
-#define DSERR_SENDLOOP MAKE_DSHRESULT(200)
-
-// The GUID specified in an audiopath file does not match a valid MIXIN buffer
-#define DSERR_BADSENDBUFFERGUID MAKE_DSHRESULT(210)
-
-// The object requested was not found (numerically equal to DMUS_E_NOT_FOUND)
-#define DSERR_OBJECTNOTFOUND MAKE_DSHRESULT(4449)
-
-// The effects requested could not be found on the system, or they were found
-// but in the wrong order, or in the wrong hardware/software locations.
-#define DSERR_FXUNAVAILABLE MAKE_DSHRESULT(220)
-
-//
-// Flags
-//
-
-#define DSCAPS_PRIMARYMONO 0x00000001
-#define DSCAPS_PRIMARYSTEREO 0x00000002
-#define DSCAPS_PRIMARY8BIT 0x00000004
-#define DSCAPS_PRIMARY16BIT 0x00000008
-#define DSCAPS_CONTINUOUSRATE 0x00000010
-#define DSCAPS_EMULDRIVER 0x00000020
-#define DSCAPS_CERTIFIED 0x00000040
-#define DSCAPS_SECONDARYMONO 0x00000100
-#define DSCAPS_SECONDARYSTEREO 0x00000200
-#define DSCAPS_SECONDARY8BIT 0x00000400
-#define DSCAPS_SECONDARY16BIT 0x00000800
-
-#define DSSCL_NORMAL 0x00000001
-#define DSSCL_PRIORITY 0x00000002
-#define DSSCL_EXCLUSIVE 0x00000003
-#define DSSCL_WRITEPRIMARY 0x00000004
-
-#define DSSPEAKER_DIRECTOUT 0x00000000
-#define DSSPEAKER_HEADPHONE 0x00000001
-#define DSSPEAKER_MONO 0x00000002
-#define DSSPEAKER_QUAD 0x00000003
-#define DSSPEAKER_STEREO 0x00000004
-#define DSSPEAKER_SURROUND 0x00000005
-#define DSSPEAKER_5POINT1 0x00000006 // obsolete 5.1 setting
-#define DSSPEAKER_7POINT1 0x00000007 // obsolete 7.1 setting
-#define DSSPEAKER_7POINT1_SURROUND 0x00000008 // correct 7.1 Home Theater setting
-#define DSSPEAKER_7POINT1_WIDE DSSPEAKER_7POINT1
-#if (DIRECTSOUND_VERSION >= 0x1000)
- #define DSSPEAKER_5POINT1_SURROUND 0x00000009 // correct 5.1 setting
- #define DSSPEAKER_5POINT1_BACK DSSPEAKER_5POINT1
-#endif
-
-#define DSSPEAKER_GEOMETRY_MIN 0x00000005 // 5 degrees
-#define DSSPEAKER_GEOMETRY_NARROW 0x0000000A // 10 degrees
-#define DSSPEAKER_GEOMETRY_WIDE 0x00000014 // 20 degrees
-#define DSSPEAKER_GEOMETRY_MAX 0x000000B4 // 180 degrees
-
-#define DSSPEAKER_COMBINED(c, g) ((DWORD)(((BYTE)(c)) | ((DWORD)((BYTE)(g))) << 16))
-#define DSSPEAKER_CONFIG(a) ((BYTE)(a))
-#define DSSPEAKER_GEOMETRY(a) ((BYTE)(((DWORD)(a) >> 16) & 0x00FF))
-
-#define DSBCAPS_PRIMARYBUFFER 0x00000001
-#define DSBCAPS_STATIC 0x00000002
-#define DSBCAPS_LOCHARDWARE 0x00000004
-#define DSBCAPS_LOCSOFTWARE 0x00000008
-#define DSBCAPS_CTRL3D 0x00000010
-#define DSBCAPS_CTRLFREQUENCY 0x00000020
-#define DSBCAPS_CTRLPAN 0x00000040
-#define DSBCAPS_CTRLVOLUME 0x00000080
-#define DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100
-#define DSBCAPS_CTRLFX 0x00000200
-#define DSBCAPS_STICKYFOCUS 0x00004000
-#define DSBCAPS_GLOBALFOCUS 0x00008000
-#define DSBCAPS_GETCURRENTPOSITION2 0x00010000
-#define DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000
-#define DSBCAPS_LOCDEFER 0x00040000
-#if (DIRECTSOUND_VERSION >= 0x1000)
- // Force GetCurrentPosition() to return a buffer's true play position;
- // unmodified by aids to enhance backward compatibility.
- #define DSBCAPS_TRUEPLAYPOSITION 0x00080000
-#endif
-
-#define DSBPLAY_LOOPING 0x00000001
-#define DSBPLAY_LOCHARDWARE 0x00000002
-#define DSBPLAY_LOCSOFTWARE 0x00000004
-#define DSBPLAY_TERMINATEBY_TIME 0x00000008
-#define DSBPLAY_TERMINATEBY_DISTANCE 0x000000010
-#define DSBPLAY_TERMINATEBY_PRIORITY 0x000000020
-
-#define DSBSTATUS_PLAYING 0x00000001
-#define DSBSTATUS_BUFFERLOST 0x00000002
-#define DSBSTATUS_LOOPING 0x00000004
-#define DSBSTATUS_LOCHARDWARE 0x00000008
-#define DSBSTATUS_LOCSOFTWARE 0x00000010
-#define DSBSTATUS_TERMINATED 0x00000020
-
-#define DSBLOCK_FROMWRITECURSOR 0x00000001
-#define DSBLOCK_ENTIREBUFFER 0x00000002
-
-#define DSBFREQUENCY_ORIGINAL 0
-#define DSBFREQUENCY_MIN 100
-#if DIRECTSOUND_VERSION >= 0x0900
-#define DSBFREQUENCY_MAX 200000
-#else
-#define DSBFREQUENCY_MAX 100000
-#endif
-
-#define DSBPAN_LEFT -10000
-#define DSBPAN_CENTER 0
-#define DSBPAN_RIGHT 10000
-
-#define DSBVOLUME_MIN -10000
-#define DSBVOLUME_MAX 0
-
-#define DSBSIZE_MIN 4
-#define DSBSIZE_MAX 0x0FFFFFFF
-#define DSBSIZE_FX_MIN 150 // NOTE: Milliseconds, not bytes
-
-#define DSBNOTIFICATIONS_MAX 100000UL
-
-#define DS3DMODE_NORMAL 0x00000000
-#define DS3DMODE_HEADRELATIVE 0x00000001
-#define DS3DMODE_DISABLE 0x00000002
-
-#define DS3D_IMMEDIATE 0x00000000
-#define DS3D_DEFERRED 0x00000001
-
-#define DS3D_MINDISTANCEFACTOR FLT_MIN
-#define DS3D_MAXDISTANCEFACTOR FLT_MAX
-#define DS3D_DEFAULTDISTANCEFACTOR 1.0f
-
-#define DS3D_MINROLLOFFFACTOR 0.0f
-#define DS3D_MAXROLLOFFFACTOR 10.0f
-#define DS3D_DEFAULTROLLOFFFACTOR 1.0f
-
-#define DS3D_MINDOPPLERFACTOR 0.0f
-#define DS3D_MAXDOPPLERFACTOR 10.0f
-#define DS3D_DEFAULTDOPPLERFACTOR 1.0f
-
-#define DS3D_DEFAULTMINDISTANCE 1.0f
-#define DS3D_DEFAULTMAXDISTANCE 1000000000.0f
-
-#define DS3D_MINCONEANGLE 0
-#define DS3D_MAXCONEANGLE 360
-#define DS3D_DEFAULTCONEANGLE 360
-
-#define DS3D_DEFAULTCONEOUTSIDEVOLUME DSBVOLUME_MAX
-
-// IDirectSoundCapture attributes
-
-#define DSCCAPS_EMULDRIVER DSCAPS_EMULDRIVER
-#define DSCCAPS_CERTIFIED DSCAPS_CERTIFIED
-#define DSCCAPS_MULTIPLECAPTURE 0x00000001
-
-// IDirectSoundCaptureBuffer attributes
-
-#define DSCBCAPS_WAVEMAPPED 0x80000000
-
-#if DIRECTSOUND_VERSION >= 0x0800
-#define DSCBCAPS_CTRLFX 0x00000200
-#endif
-
-
-#define DSCBLOCK_ENTIREBUFFER 0x00000001
-
-#define DSCBSTATUS_CAPTURING 0x00000001
-#define DSCBSTATUS_LOOPING 0x00000002
-
-#define DSCBSTART_LOOPING 0x00000001
-
-#define DSBPN_OFFSETSTOP 0xFFFFFFFF
-
-#define DS_CERTIFIED 0x00000000
-#define DS_UNCERTIFIED 0x00000001
-
-
-//
-// Flags for the I3DL2 effects
-//
-
-//
-// I3DL2 Material Presets
-//
-
-enum
-{
- DSFX_I3DL2_MATERIAL_PRESET_SINGLEWINDOW,
- DSFX_I3DL2_MATERIAL_PRESET_DOUBLEWINDOW,
- DSFX_I3DL2_MATERIAL_PRESET_THINDOOR,
- DSFX_I3DL2_MATERIAL_PRESET_THICKDOOR,
- DSFX_I3DL2_MATERIAL_PRESET_WOODWALL,
- DSFX_I3DL2_MATERIAL_PRESET_BRICKWALL,
- DSFX_I3DL2_MATERIAL_PRESET_STONEWALL,
- DSFX_I3DL2_MATERIAL_PRESET_CURTAIN
-};
-
-#define I3DL2_MATERIAL_PRESET_SINGLEWINDOW -2800,0.71f
-#define I3DL2_MATERIAL_PRESET_DOUBLEWINDOW -5000,0.40f
-#define I3DL2_MATERIAL_PRESET_THINDOOR -1800,0.66f
-#define I3DL2_MATERIAL_PRESET_THICKDOOR -4400,0.64f
-#define I3DL2_MATERIAL_PRESET_WOODWALL -4000,0.50f
-#define I3DL2_MATERIAL_PRESET_BRICKWALL -5000,0.60f
-#define I3DL2_MATERIAL_PRESET_STONEWALL -6000,0.68f
-#define I3DL2_MATERIAL_PRESET_CURTAIN -1200,0.15f
-
-enum
-{
- DSFX_I3DL2_ENVIRONMENT_PRESET_DEFAULT,
- DSFX_I3DL2_ENVIRONMENT_PRESET_GENERIC,
- DSFX_I3DL2_ENVIRONMENT_PRESET_PADDEDCELL,
- DSFX_I3DL2_ENVIRONMENT_PRESET_ROOM,
- DSFX_I3DL2_ENVIRONMENT_PRESET_BATHROOM,
- DSFX_I3DL2_ENVIRONMENT_PRESET_LIVINGROOM,
- DSFX_I3DL2_ENVIRONMENT_PRESET_STONEROOM,
- DSFX_I3DL2_ENVIRONMENT_PRESET_AUDITORIUM,
- DSFX_I3DL2_ENVIRONMENT_PRESET_CONCERTHALL,
- DSFX_I3DL2_ENVIRONMENT_PRESET_CAVE,
- DSFX_I3DL2_ENVIRONMENT_PRESET_ARENA,
- DSFX_I3DL2_ENVIRONMENT_PRESET_HANGAR,
- DSFX_I3DL2_ENVIRONMENT_PRESET_CARPETEDHALLWAY,
- DSFX_I3DL2_ENVIRONMENT_PRESET_HALLWAY,
- DSFX_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR,
- DSFX_I3DL2_ENVIRONMENT_PRESET_ALLEY,
- DSFX_I3DL2_ENVIRONMENT_PRESET_FOREST,
- DSFX_I3DL2_ENVIRONMENT_PRESET_CITY,
- DSFX_I3DL2_ENVIRONMENT_PRESET_MOUNTAINS,
- DSFX_I3DL2_ENVIRONMENT_PRESET_QUARRY,
- DSFX_I3DL2_ENVIRONMENT_PRESET_PLAIN,
- DSFX_I3DL2_ENVIRONMENT_PRESET_PARKINGLOT,
- DSFX_I3DL2_ENVIRONMENT_PRESET_SEWERPIPE,
- DSFX_I3DL2_ENVIRONMENT_PRESET_UNDERWATER,
- DSFX_I3DL2_ENVIRONMENT_PRESET_SMALLROOM,
- DSFX_I3DL2_ENVIRONMENT_PRESET_MEDIUMROOM,
- DSFX_I3DL2_ENVIRONMENT_PRESET_LARGEROOM,
- DSFX_I3DL2_ENVIRONMENT_PRESET_MEDIUMHALL,
- DSFX_I3DL2_ENVIRONMENT_PRESET_LARGEHALL,
- DSFX_I3DL2_ENVIRONMENT_PRESET_PLATE
-};
-
-//
-// I3DL2 Reverberation Presets Values
-//
-
-#define I3DL2_ENVIRONMENT_PRESET_DEFAULT -1000, -100, 0.0f, 1.49f, 0.83f, -2602, 0.007f, 200, 0.011f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_GENERIC -1000, -100, 0.0f, 1.49f, 0.83f, -2602, 0.007f, 200, 0.011f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_PADDEDCELL -1000,-6000, 0.0f, 0.17f, 0.10f, -1204, 0.001f, 207, 0.002f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_ROOM -1000, -454, 0.0f, 0.40f, 0.83f, -1646, 0.002f, 53, 0.003f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_BATHROOM -1000,-1200, 0.0f, 1.49f, 0.54f, -370, 0.007f, 1030, 0.011f, 100.0f, 60.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_LIVINGROOM -1000,-6000, 0.0f, 0.50f, 0.10f, -1376, 0.003f, -1104, 0.004f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_STONEROOM -1000, -300, 0.0f, 2.31f, 0.64f, -711, 0.012f, 83, 0.017f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_AUDITORIUM -1000, -476, 0.0f, 4.32f, 0.59f, -789, 0.020f, -289, 0.030f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_CONCERTHALL -1000, -500, 0.0f, 3.92f, 0.70f, -1230, 0.020f, -2, 0.029f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_CAVE -1000, 0, 0.0f, 2.91f, 1.30f, -602, 0.015f, -302, 0.022f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_ARENA -1000, -698, 0.0f, 7.24f, 0.33f, -1166, 0.020f, 16, 0.030f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_HANGAR -1000,-1000, 0.0f,10.05f, 0.23f, -602, 0.020f, 198, 0.030f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_CARPETEDHALLWAY -1000,-4000, 0.0f, 0.30f, 0.10f, -1831, 0.002f, -1630, 0.030f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_HALLWAY -1000, -300, 0.0f, 1.49f, 0.59f, -1219, 0.007f, 441, 0.011f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR -1000, -237, 0.0f, 2.70f, 0.79f, -1214, 0.013f, 395, 0.020f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_ALLEY -1000, -270, 0.0f, 1.49f, 0.86f, -1204, 0.007f, -4, 0.011f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_FOREST -1000,-3300, 0.0f, 1.49f, 0.54f, -2560, 0.162f, -613, 0.088f, 79.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_CITY -1000, -800, 0.0f, 1.49f, 0.67f, -2273, 0.007f, -2217, 0.011f, 50.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_MOUNTAINS -1000,-2500, 0.0f, 1.49f, 0.21f, -2780, 0.300f, -2014, 0.100f, 27.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_QUARRY -1000,-1000, 0.0f, 1.49f, 0.83f,-10000, 0.061f, 500, 0.025f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_PLAIN -1000,-2000, 0.0f, 1.49f, 0.50f, -2466, 0.179f, -2514, 0.100f, 21.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_PARKINGLOT -1000, 0, 0.0f, 1.65f, 1.50f, -1363, 0.008f, -1153, 0.012f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_SEWERPIPE -1000,-1000, 0.0f, 2.81f, 0.14f, 429, 0.014f, 648, 0.021f, 80.0f, 60.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_UNDERWATER -1000,-4000, 0.0f, 1.49f, 0.10f, -449, 0.007f, 1700, 0.011f, 100.0f, 100.0f, 5000.0f
-
-//
-// Examples simulating 'musical' reverb presets
-//
-// Name Decay time Description
-// Small Room 1.1s A small size room with a length of 5m or so.
-// Medium Room 1.3s A medium size room with a length of 10m or so.
-// Large Room 1.5s A large size room suitable for live performances.
-// Medium Hall 1.8s A medium size concert hall.
-// Large Hall 1.8s A large size concert hall suitable for a full orchestra.
-// Plate 1.3s A plate reverb simulation.
-//
-
-#define I3DL2_ENVIRONMENT_PRESET_SMALLROOM -1000, -600, 0.0f, 1.10f, 0.83f, -400, 0.005f, 500, 0.010f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_MEDIUMROOM -1000, -600, 0.0f, 1.30f, 0.83f, -1000, 0.010f, -200, 0.020f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_LARGEROOM -1000, -600, 0.0f, 1.50f, 0.83f, -1600, 0.020f, -1000, 0.040f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_MEDIUMHALL -1000, -600, 0.0f, 1.80f, 0.70f, -1300, 0.015f, -800, 0.030f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_LARGEHALL -1000, -600, 0.0f, 1.80f, 0.70f, -2000, 0.030f, -1400, 0.060f, 100.0f, 100.0f, 5000.0f
-#define I3DL2_ENVIRONMENT_PRESET_PLATE -1000, -200, 0.0f, 1.30f, 0.90f, 0, 0.002f, 0, 0.010f, 100.0f, 75.0f, 5000.0f
-
-//
-// DirectSound3D Algorithms
-//
-
-// Default DirectSound3D algorithm {00000000-0000-0000-0000-000000000000}
-#define DS3DALG_DEFAULT GUID_NULL
-
-// No virtualization (Pan3D) {C241333F-1C1B-11d2-94F5-00C04FC28ACA}
-DEFINE_GUID(DS3DALG_NO_VIRTUALIZATION, 0xc241333f, 0x1c1b, 0x11d2, 0x94, 0xf5, 0x0, 0xc0, 0x4f, 0xc2, 0x8a, 0xca);
-
-// High-quality HRTF algorithm {C2413340-1C1B-11d2-94F5-00C04FC28ACA}
-DEFINE_GUID(DS3DALG_HRTF_FULL, 0xc2413340, 0x1c1b, 0x11d2, 0x94, 0xf5, 0x0, 0xc0, 0x4f, 0xc2, 0x8a, 0xca);
-
-// Lower-quality HRTF algorithm {C2413342-1C1B-11d2-94F5-00C04FC28ACA}
-DEFINE_GUID(DS3DALG_HRTF_LIGHT, 0xc2413342, 0x1c1b, 0x11d2, 0x94, 0xf5, 0x0, 0xc0, 0x4f, 0xc2, 0x8a, 0xca);
-
-
-#if DIRECTSOUND_VERSION >= 0x0800
-
-//
-// DirectSound Internal Effect Algorithms
-//
-
-
-// Gargle {DAFD8210-5711-4B91-9FE3-F75B7AE279BF}
-DEFINE_GUID(GUID_DSFX_STANDARD_GARGLE, 0xdafd8210, 0x5711, 0x4b91, 0x9f, 0xe3, 0xf7, 0x5b, 0x7a, 0xe2, 0x79, 0xbf);
-
-// Chorus {EFE6629C-81F7-4281-BD91-C9D604A95AF6}
-DEFINE_GUID(GUID_DSFX_STANDARD_CHORUS, 0xefe6629c, 0x81f7, 0x4281, 0xbd, 0x91, 0xc9, 0xd6, 0x04, 0xa9, 0x5a, 0xf6);
-
-// Flanger {EFCA3D92-DFD8-4672-A603-7420894BAD98}
-DEFINE_GUID(GUID_DSFX_STANDARD_FLANGER, 0xefca3d92, 0xdfd8, 0x4672, 0xa6, 0x03, 0x74, 0x20, 0x89, 0x4b, 0xad, 0x98);
-
-// Echo/Delay {EF3E932C-D40B-4F51-8CCF-3F98F1B29D5D}
-DEFINE_GUID(GUID_DSFX_STANDARD_ECHO, 0xef3e932c, 0xd40b, 0x4f51, 0x8c, 0xcf, 0x3f, 0x98, 0xf1, 0xb2, 0x9d, 0x5d);
-
-// Distortion {EF114C90-CD1D-484E-96E5-09CFAF912A21}
-DEFINE_GUID(GUID_DSFX_STANDARD_DISTORTION, 0xef114c90, 0xcd1d, 0x484e, 0x96, 0xe5, 0x09, 0xcf, 0xaf, 0x91, 0x2a, 0x21);
-
-// Compressor/Limiter {EF011F79-4000-406D-87AF-BFFB3FC39D57}
-DEFINE_GUID(GUID_DSFX_STANDARD_COMPRESSOR, 0xef011f79, 0x4000, 0x406d, 0x87, 0xaf, 0xbf, 0xfb, 0x3f, 0xc3, 0x9d, 0x57);
-
-// Parametric Equalization {120CED89-3BF4-4173-A132-3CB406CF3231}
-DEFINE_GUID(GUID_DSFX_STANDARD_PARAMEQ, 0x120ced89, 0x3bf4, 0x4173, 0xa1, 0x32, 0x3c, 0xb4, 0x06, 0xcf, 0x32, 0x31);
-
-// I3DL2 Environmental Reverberation: Reverb (Listener) Effect {EF985E71-D5C7-42D4-BA4D-2D073E2E96F4}
-DEFINE_GUID(GUID_DSFX_STANDARD_I3DL2REVERB, 0xef985e71, 0xd5c7, 0x42d4, 0xba, 0x4d, 0x2d, 0x07, 0x3e, 0x2e, 0x96, 0xf4);
-
-// Waves Reverberation {87FC0268-9A55-4360-95AA-004A1D9DE26C}
-DEFINE_GUID(GUID_DSFX_WAVES_REVERB, 0x87fc0268, 0x9a55, 0x4360, 0x95, 0xaa, 0x00, 0x4a, 0x1d, 0x9d, 0xe2, 0x6c);
-
-//
-// DirectSound Capture Effect Algorithms
-//
-
-
-// Acoustic Echo Canceller {BF963D80-C559-11D0-8A2B-00A0C9255AC1}
-// Matches KSNODETYPE_ACOUSTIC_ECHO_CANCEL in ksmedia.h
-DEFINE_GUID(GUID_DSCFX_CLASS_AEC, 0xBF963D80L, 0xC559, 0x11D0, 0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1);
-
-// Microsoft AEC {CDEBB919-379A-488a-8765-F53CFD36DE40}
-DEFINE_GUID(GUID_DSCFX_MS_AEC, 0xcdebb919, 0x379a, 0x488a, 0x87, 0x65, 0xf5, 0x3c, 0xfd, 0x36, 0xde, 0x40);
-
-// System AEC {1C22C56D-9879-4f5b-A389-27996DDC2810}
-DEFINE_GUID(GUID_DSCFX_SYSTEM_AEC, 0x1c22c56d, 0x9879, 0x4f5b, 0xa3, 0x89, 0x27, 0x99, 0x6d, 0xdc, 0x28, 0x10);
-
-// Noise Supression {E07F903F-62FD-4e60-8CDD-DEA7236665B5}
-// Matches KSNODETYPE_NOISE_SUPPRESS in post Windows ME DDK's ksmedia.h
-DEFINE_GUID(GUID_DSCFX_CLASS_NS, 0xe07f903f, 0x62fd, 0x4e60, 0x8c, 0xdd, 0xde, 0xa7, 0x23, 0x66, 0x65, 0xb5);
-
-// Microsoft Noise Suppresion {11C5C73B-66E9-4ba1-A0BA-E814C6EED92D}
-DEFINE_GUID(GUID_DSCFX_MS_NS, 0x11c5c73b, 0x66e9, 0x4ba1, 0xa0, 0xba, 0xe8, 0x14, 0xc6, 0xee, 0xd9, 0x2d);
-
-// System Noise Suppresion {5AB0882E-7274-4516-877D-4EEE99BA4FD0}
-DEFINE_GUID(GUID_DSCFX_SYSTEM_NS, 0x5ab0882e, 0x7274, 0x4516, 0x87, 0x7d, 0x4e, 0xee, 0x99, 0xba, 0x4f, 0xd0);
-
-#endif // DIRECTSOUND_VERSION >= 0x0800
-
-#endif // __DSOUND_INCLUDED__
-
-
-
-#ifdef __cplusplus
-};
-#endif // __cplusplus
-
+++ /dev/null
-#pragma once
-
-/*++
-
-Copyright (c) Microsoft Corporation. All rights reserved.
-
-Module Name:
-
- devpkey.h
-
-Abstract:
-
- Defines property keys for the Plug and Play Device Property API.
-
-Author:
-
- Jim Cavalaris (jamesca) 10-14-2003
-
-Environment:
-
- User-mode only.
-
-Revision History:
-
- 14-October-2003 jamesca
-
- Creation and initial implementation.
-
- 20-June-2006 dougb
-
- Copied Jim's version replaced "DEFINE_DEVPROPKEY(DEVPKEY_" with "DEFINE_PROPERTYKEY(PKEY_"
-
---*/
-
-//#include <devpropdef.h>
-
-//
-// _NAME
-//
-
-DEFINE_PROPERTYKEY(PKEY_NAME, 0xb725f130, 0x47ef, 0x101a, 0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac, 10); // DEVPROP_TYPE_STRING
-
-//
-// Device properties
-// These PKEYs correspond to the old setupapi SPDRP_XXX properties
-//
-DEFINE_PROPERTYKEY(PKEY_Device_DeviceDesc, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 2); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_HardwareIds, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 3); // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_CompatibleIds, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 4); // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_Service, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 6); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_Class, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 9); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_ClassGuid, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 10); // DEVPROP_TYPE_GUID
-DEFINE_PROPERTYKEY(PKEY_Device_Driver, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 11); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_ConfigFlags, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 12); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_Manufacturer, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 13); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_LocationInfo, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 15); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_PDOName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 16); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_Capabilities, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 17); // DEVPROP_TYPE_UNINT32
-DEFINE_PROPERTYKEY(PKEY_Device_UINumber, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 18); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_UpperFilters, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 19); // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_LowerFilters, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 20); // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_BusTypeGuid, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 21); // DEVPROP_TYPE_GUID
-DEFINE_PROPERTYKEY(PKEY_Device_LegacyBusType, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 22); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_BusNumber, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 23); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_EnumeratorName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 24); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_Security, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 25); // DEVPROP_TYPE_SECURITY_DESCRIPTOR
-DEFINE_PROPERTYKEY(PKEY_Device_SecuritySDS, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 26); // DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DevType, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 27); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_Exclusive, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 28); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_Characteristics, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 29); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_Address, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 30); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_UINumberDescFormat, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 31); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_PowerData, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 32); // DEVPROP_TYPE_BINARY
-DEFINE_PROPERTYKEY(PKEY_Device_RemovalPolicy, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 33); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_RemovalPolicyDefault, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 34); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_RemovalPolicyOverride, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 35); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_InstallState, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 36); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_LocationPaths, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 37); // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_BaseContainerId, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 38); // DEVPROP_TYPE_GUID
-
-//
-// Device properties
-// These PKEYs correspond to a device's status and problem code
-//
-DEFINE_PROPERTYKEY(PKEY_Device_DevNodeStatus, 0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 2); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_ProblemCode, 0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 3); // DEVPROP_TYPE_UINT32
-
-//
-// Device properties
-// These PKEYs correspond to device relations
-//
-DEFINE_PROPERTYKEY(PKEY_Device_EjectionRelations, 0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 4); // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_RemovalRelations, 0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 5); // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_PowerRelations, 0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 6); // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_BusRelations, 0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 7); // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_Parent, 0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 8); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_Children, 0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 9); // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_Siblings, 0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 10); // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_TransportRelations, 0x4340a6c5, 0x93fa, 0x4706, 0x97, 0x2c, 0x7b, 0x64, 0x80, 0x08, 0xa5, 0xa7, 11); // DEVPROP_TYPE_STRING_LIST
-
-//
-// Other Device properties
-//
-DEFINE_PROPERTYKEY(PKEY_Device_Reported, 0x80497100, 0x8c73, 0x48b9, 0xaa, 0xd9, 0xce, 0x38, 0x7e, 0x19, 0xc5, 0x6e, 2); // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_Device_Legacy, 0x80497100, 0x8c73, 0x48b9, 0xaa, 0xd9, 0xce, 0x38, 0x7e, 0x19, 0xc5, 0x6e, 3); // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_Device_InstanceId, 0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57, 256); // DEVPROP_TYPE_STRING
-
-DEFINE_PROPERTYKEY(PKEY_Device_ContainerId, 0x8c7ed206, 0x3f8a, 0x4827, 0xb3, 0xab, 0xae, 0x9e, 0x1f, 0xae, 0xfc, 0x6c, 2); // DEVPROP_TYPE_GUID
-
-DEFINE_PROPERTYKEY(PKEY_Device_ModelId, 0x80d81ea6, 0x7473, 0x4b0c, 0x82, 0x16, 0xef, 0xc1, 0x1a, 0x2c, 0x4c, 0x8b, 2); // DEVPROP_TYPE_GUID
-
-DEFINE_PROPERTYKEY(PKEY_Device_FriendlyNameAttributes, 0x80d81ea6, 0x7473, 0x4b0c, 0x82, 0x16, 0xef, 0xc1, 0x1a, 0x2c, 0x4c, 0x8b, 3); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_ManufacturerAttributes, 0x80d81ea6, 0x7473, 0x4b0c, 0x82, 0x16, 0xef, 0xc1, 0x1a, 0x2c, 0x4c, 0x8b, 4); // DEVPROP_TYPE_UINT32
-
-DEFINE_PROPERTYKEY(PKEY_Device_PresenceNotForDevice, 0x80d81ea6, 0x7473, 0x4b0c, 0x82, 0x16, 0xef, 0xc1, 0x1a, 0x2c, 0x4c, 0x8b, 5); // DEVPROP_TYPE_BOOLEAN
-
-
-DEFINE_PROPERTYKEY(PKEY_Numa_Proximity_Domain, 0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2, 1); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_DHP_Rebalance_Policy, 0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2, 2); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_Numa_Node, 0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2, 3); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_BusReportedDeviceDesc, 0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2, 4); // DEVPROP_TYPE_STRING
-
-DEFINE_PROPERTYKEY(PKEY_Device_InstallInProgress, 0x83da6326, 0x97a6, 0x4088, 0x94, 0x53, 0xa1, 0x92, 0x3f, 0x57, 0x3b, 0x29, 9); // DEVPROP_TYPE_BOOLEAN
-
-//
-// Device driver properties
-//
-DEFINE_PROPERTYKEY(PKEY_Device_DriverDate, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 2); // DEVPROP_TYPE_FILETIME
-DEFINE_PROPERTYKEY(PKEY_Device_DriverVersion, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 3); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DriverDesc, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 4); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DriverInfPath, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 5); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DriverInfSection, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 6); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DriverInfSectionExt, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 7); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_MatchingDeviceId, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 8); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DriverProvider, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 9); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DriverPropPageProvider, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 10); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DriverCoInstallers, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 11); // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_Device_ResourcePickerTags, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 12); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_ResourcePickerExceptions, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 13); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_Device_DriverRank, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 14); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_DriverLogoLevel, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 15); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_Device_NoConnectSound, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 17); // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_Device_GenericDriverInstalled, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 18); // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_Device_AdditionalSoftwareRequested, 0xa8b865dd, 0x2e3d, 0x4094, 0xad, 0x97, 0xe5, 0x93, 0xa7, 0xc, 0x75, 0xd6, 19);// DEVPROP_TYPE_BOOLEAN
-
-//
-// Device safe-removal properties
-//
-DEFINE_PROPERTYKEY(PKEY_Device_SafeRemovalRequired, 0xafd97640, 0x86a3, 0x4210, 0xb6, 0x7c, 0x28, 0x9c, 0x41, 0xaa, 0xbe, 0x55, 2); // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_Device_SafeRemovalRequiredOverride, 0xafd97640, 0x86a3, 0x4210, 0xb6, 0x7c, 0x28, 0x9c, 0x41, 0xaa, 0xbe, 0x55, 3);// DEVPROP_TYPE_BOOLEAN
-
-
-//
-// Device properties that were set by the driver package that was installed
-// on the device.
-//
-DEFINE_PROPERTYKEY(PKEY_DrvPkg_Model, 0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 2); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DrvPkg_VendorWebSite, 0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 3); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DrvPkg_DetailedDescription, 0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 4); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DrvPkg_DocumentationLink, 0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 5); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DrvPkg_Icon, 0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 6); // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_DrvPkg_BrandingIcon, 0xcf73bb51, 0x3abf, 0x44a2, 0x85, 0xe0, 0x9a, 0x3d, 0xc7, 0xa1, 0x21, 0x32, 7); // DEVPROP_TYPE_STRING_LIST
-
-//
-// Device setup class properties
-// These PKEYs correspond to the old setupapi SPCRP_XXX properties
-//
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_UpperFilters, 0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 19); // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_LowerFilters, 0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 20); // DEVPROP_TYPE_STRING_LIST
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_Security, 0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 25); // DEVPROP_TYPE_SECURITY_DESCRIPTOR
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_SecuritySDS, 0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 26); // DEVPROP_TYPE_SECURITY_DESCRIPTOR_STRING
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_DevType, 0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 27); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_Exclusive, 0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 28); // DEVPROP_TYPE_UINT32
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_Characteristics, 0x4321918b, 0xf69e, 0x470d, 0xa5, 0xde, 0x4d, 0x88, 0xc7, 0x5a, 0xd2, 0x4b, 29); // DEVPROP_TYPE_UINT32
-
-//
-// Device setup class properties
-// These PKEYs correspond to registry values under the device class GUID key
-//
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_Name, 0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 2); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_ClassName, 0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 3); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_Icon, 0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 4); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_ClassInstaller, 0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 5); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_PropPageProvider, 0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 6); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_NoInstallClass, 0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 7); // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_NoDisplayClass, 0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 8); // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_SilentInstall, 0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 9); // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_NoUseClass, 0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 10); // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_DefaultService, 0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 11); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_IconPath, 0x259abffc, 0x50a7, 0x47ce, 0xaf, 0x8, 0x68, 0xc9, 0xa7, 0xd7, 0x33, 0x66, 12); // DEVPROP_TYPE_STRING_LIST
-
-//
-// Other Device setup class properties
-//
-DEFINE_PROPERTYKEY(PKEY_DeviceClass_ClassCoInstallers, 0x713d1703, 0xa2e2, 0x49f5, 0x92, 0x14, 0x56, 0x47, 0x2e, 0xf3, 0xda, 0x5c, 2); // DEVPROP_TYPE_STRING_LIST
-
-//
-// Device interface properties
-//
-DEFINE_PROPERTYKEY(PKEY_DeviceInterface_FriendlyName, 0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22, 2); // DEVPROP_TYPE_STRING
-DEFINE_PROPERTYKEY(PKEY_DeviceInterface_Enabled, 0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22, 3); // DEVPROP_TYPE_BOOLEAN
-DEFINE_PROPERTYKEY(PKEY_DeviceInterface_ClassGuid, 0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22, 4); // DEVPROP_TYPE_GUID
-
-//
-// Device interface class properties
-//
-DEFINE_PROPERTYKEY(PKEY_DeviceInterfaceClass_DefaultInterface, 0x14c83a99, 0x0b3f, 0x44b7, 0xbe, 0x4c, 0xa1, 0x78, 0xd3, 0x99, 0x05, 0x64, 2); // DEVPROP_TYPE_STRING
-
-
-
-
+++ /dev/null
-#ifndef __gInclude__\r
-#define __gInclude__\r
-\r
-#if SGI \r
- #undef BEOS \r
- #undef MAC \r
- #undef WINDOWS\r
- //\r
- #define ASIO_BIG_ENDIAN 1\r
- #define ASIO_CPU_MIPS 1\r
-#elif defined(_WIN32) || defined(_WIN64)\r
- #undef BEOS \r
- #undef MAC \r
- #undef SGI\r
- #define WINDOWS 1\r
- #define ASIO_LITTLE_ENDIAN 1\r
- #define ASIO_CPU_X86 1\r
-#elif BEOS\r
- #undef MAC \r
- #undef SGI\r
- #undef WINDOWS\r
- #define ASIO_LITTLE_ENDIAN 1\r
- #define ASIO_CPU_X86 1\r
- //\r
-#else\r
- #define MAC 1\r
- #undef BEOS \r
- #undef WINDOWS\r
- #undef SGI\r
- #define ASIO_BIG_ENDIAN 1\r
- #define ASIO_CPU_PPC 1\r
-#endif\r
-\r
-// always\r
-#define NATIVE_INT64 0\r
-#define IEEE754_64FLOAT 1\r
-\r
-#endif // __gInclude__\r
+++ /dev/null
-#include "asiosys.h"\r
-#include "asio.h"\r
-\r
-/* Forward Declarations */ \r
-\r
-#ifndef __ASIODRIVER_FWD_DEFINED__\r
-#define __ASIODRIVER_FWD_DEFINED__\r
-typedef interface IASIO IASIO;\r
-#endif /* __ASIODRIVER_FWD_DEFINED__ */\r
-\r
-interface IASIO : public IUnknown\r
-{\r
-\r
- virtual ASIOBool init(void *sysHandle) = 0;\r
- virtual void getDriverName(char *name) = 0; \r
- virtual long getDriverVersion() = 0;\r
- virtual void getErrorMessage(char *string) = 0; \r
- virtual ASIOError start() = 0;\r
- virtual ASIOError stop() = 0;\r
- virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels) = 0;\r
- virtual ASIOError getLatencies(long *inputLatency, long *outputLatency) = 0;\r
- virtual ASIOError getBufferSize(long *minSize, long *maxSize,\r
- long *preferredSize, long *granularity) = 0;\r
- virtual ASIOError canSampleRate(ASIOSampleRate sampleRate) = 0;\r
- virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate) = 0;\r
- virtual ASIOError setSampleRate(ASIOSampleRate sampleRate) = 0;\r
- virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources) = 0;\r
- virtual ASIOError setClockSource(long reference) = 0;\r
- virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0;\r
- virtual ASIOError getChannelInfo(ASIOChannelInfo *info) = 0;\r
- virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels,\r
- long bufferSize, ASIOCallbacks *callbacks) = 0;\r
- virtual ASIOError disposeBuffers() = 0;\r
- virtual ASIOError controlPanel() = 0;\r
- virtual ASIOError future(long selector,void *opt) = 0;\r
- virtual ASIOError outputReady() = 0;\r
-};\r
+++ /dev/null
-/*
- IASIOThiscallResolver.cpp see the comments in iasiothiscallresolver.h for
- the top level description - this comment describes the technical details of
- the implementation.
-
- The latest version of this file is available from:
- http://www.audiomulch.com/~rossb/code/calliasio
-
- please email comments to Ross Bencina <rossb@audiomulch.com>
-
- BACKGROUND
-
- The IASIO interface declared in the Steinberg ASIO 2 SDK declares
- functions with no explicit calling convention. This causes MSVC++ to default
- to using the thiscall convention, which is a proprietary convention not
- implemented by some non-microsoft compilers - notably borland BCC,
- C++Builder, and gcc. MSVC++ is the defacto standard compiler used by
- Steinberg. As a result of this situation, the ASIO sdk will compile with
- any compiler, however attempting to execute the compiled code will cause a
- crash due to different default calling conventions on non-Microsoft
- compilers.
-
- IASIOThiscallResolver solves the problem by providing an adapter class that
- delegates to the IASIO interface using the correct calling convention
- (thiscall). Due to the lack of support for thiscall in the Borland and GCC
- compilers, the calls have been implemented in assembly language.
-
- A number of macros are defined for thiscall function calls with different
- numbers of parameters, with and without return values - it may be possible
- to modify the format of these macros to make them work with other inline
- assemblers.
-
-
- THISCALL DEFINITION
-
- A number of definitions of the thiscall calling convention are floating
- around the internet. The following definition has been validated against
- output from the MSVC++ compiler:
-
- For non-vararg functions, thiscall works as follows: the object (this)
- pointer is passed in ECX. All arguments are passed on the stack in
- right to left order. The return value is placed in EAX. The callee
- clears the passed arguments from the stack.
-
-
- FINDING FUNCTION POINTERS FROM AN IASIO POINTER
-
- The first field of a COM object is a pointer to its vtble. Thus a pointer
- to an object implementing the IASIO interface also points to a pointer to
- that object's vtbl. The vtble is a table of function pointers for all of
- the virtual functions exposed by the implemented interfaces.
-
- If we consider a variable declared as a pointer to IASO:
-
- IASIO *theAsioDriver
-
- theAsioDriver points to:
-
- object implementing IASIO
- {
- IASIOvtbl *vtbl
- other data
- }
-
- in other words, theAsioDriver points to a pointer to an IASIOvtbl
-
- vtbl points to a table of function pointers:
-
- IASIOvtbl ( interface IASIO : public IUnknown )
- {
- (IUnknown functions)
- 0 virtual HRESULT STDMETHODCALLTYPE (*QueryInterface)(REFIID riid, void **ppv) = 0;
- 4 virtual ULONG STDMETHODCALLTYPE (*AddRef)() = 0;
- 8 virtual ULONG STDMETHODCALLTYPE (*Release)() = 0;
-
- (IASIO functions)
- 12 virtual ASIOBool (*init)(void *sysHandle) = 0;
- 16 virtual void (*getDriverName)(char *name) = 0;
- 20 virtual long (*getDriverVersion)() = 0;
- 24 virtual void (*getErrorMessage)(char *string) = 0;
- 28 virtual ASIOError (*start)() = 0;
- 32 virtual ASIOError (*stop)() = 0;
- 36 virtual ASIOError (*getChannels)(long *numInputChannels, long *numOutputChannels) = 0;
- 40 virtual ASIOError (*getLatencies)(long *inputLatency, long *outputLatency) = 0;
- 44 virtual ASIOError (*getBufferSize)(long *minSize, long *maxSize,
- long *preferredSize, long *granularity) = 0;
- 48 virtual ASIOError (*canSampleRate)(ASIOSampleRate sampleRate) = 0;
- 52 virtual ASIOError (*getSampleRate)(ASIOSampleRate *sampleRate) = 0;
- 56 virtual ASIOError (*setSampleRate)(ASIOSampleRate sampleRate) = 0;
- 60 virtual ASIOError (*getClockSources)(ASIOClockSource *clocks, long *numSources) = 0;
- 64 virtual ASIOError (*setClockSource)(long reference) = 0;
- 68 virtual ASIOError (*getSamplePosition)(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0;
- 72 virtual ASIOError (*getChannelInfo)(ASIOChannelInfo *info) = 0;
- 76 virtual ASIOError (*createBuffers)(ASIOBufferInfo *bufferInfos, long numChannels,
- long bufferSize, ASIOCallbacks *callbacks) = 0;
- 80 virtual ASIOError (*disposeBuffers)() = 0;
- 84 virtual ASIOError (*controlPanel)() = 0;
- 88 virtual ASIOError (*future)(long selector,void *opt) = 0;
- 92 virtual ASIOError (*outputReady)() = 0;
- };
-
- The numbers in the left column show the byte offset of each function ptr
- from the beginning of the vtbl. These numbers are used in the code below
- to select different functions.
-
- In order to find the address of a particular function, theAsioDriver
- must first be dereferenced to find the value of the vtbl pointer:
-
- mov eax, theAsioDriver
- mov edx, [theAsioDriver] // edx now points to vtbl[0]
-
- Then an offset must be added to the vtbl pointer to select a
- particular function, for example vtbl+44 points to the slot containing
- a pointer to the getBufferSize function.
-
- Finally vtbl+x must be dereferenced to obtain the value of the function
- pointer stored in that address:
-
- call [edx+44] // call the function pointed to by
- // the value in the getBufferSize field of the vtbl
-
-
- SEE ALSO
-
- Martin Fay's OpenASIO DLL at http://www.martinfay.com solves the same
- problem by providing a new COM interface which wraps IASIO with an
- interface that uses portable calling conventions. OpenASIO must be compiled
- with MSVC, and requires that you ship the OpenASIO DLL with your
- application.
-
-
- ACKNOWLEDGEMENTS
-
- Ross Bencina: worked out the thiscall details above, wrote the original
- Borland asm macros, and a patch for asio.cpp (which is no longer needed).
- Thanks to Martin Fay for introducing me to the issues discussed here,
- and to Rene G. Ceballos for assisting with asm dumps from MSVC++.
-
- Antti Silvast: converted the original calliasio to work with gcc and NASM
- by implementing the asm code in a separate file.
-
- Fraser Adams: modified the original calliasio containing the Borland inline
- asm to add inline asm for gcc i.e. Intel syntax for Borland and AT&T syntax
- for gcc. This seems a neater approach for gcc than to have a separate .asm
- file and it means that we only need one version of the thiscall patch.
-
- Fraser Adams: rewrote the original calliasio patch in the form of the
- IASIOThiscallResolver class in order to avoid modifications to files from
- the Steinberg SDK, which may have had potential licence issues.
-
- Andrew Baldwin: contributed fixes for compatibility problems with more
- recent versions of the gcc assembler.
-*/
-
-
-// We only need IASIOThiscallResolver at all if we are on Win32. For other
-// platforms we simply bypass the IASIOThiscallResolver definition to allow us
-// to be safely #include'd whatever the platform to keep client code portable
-#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(_WIN64)
-
-
-// If microsoft compiler we can call IASIO directly so IASIOThiscallResolver
-// is not used.
-#if !defined(_MSC_VER)
-
-
-#include <new>
-#include <assert.h>
-
-// We have a mechanism in iasiothiscallresolver.h to ensure that asio.h is
-// #include'd before it in client code, we do NOT want to do this test here.
-#define iasiothiscallresolver_sourcefile 1
-#include "iasiothiscallresolver.h"
-#undef iasiothiscallresolver_sourcefile
-
-// iasiothiscallresolver.h redefines ASIOInit for clients, but we don't want
-// this macro defined in this translation unit.
-#undef ASIOInit
-
-
-// theAsioDriver is a global pointer to the current IASIO instance which the
-// ASIO SDK uses to perform all actions on the IASIO interface. We substitute
-// our own forwarding interface into this pointer.
-extern IASIO* theAsioDriver;
-
-
-// The following macros define the inline assembler for BORLAND first then gcc
-
-#if defined(__BCPLUSPLUS__) || defined(__BORLANDC__)
-
-
-#define CALL_THISCALL_0( resultName, thisPtr, funcOffset )\
- void *this_ = (thisPtr); \
- __asm { \
- mov ecx, this_ ; \
- mov eax, [ecx] ; \
- call [eax+funcOffset] ; \
- mov resultName, eax ; \
- }
-
-
-#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 )\
- void *this_ = (thisPtr); \
- __asm { \
- mov eax, param1 ; \
- push eax ; \
- mov ecx, this_ ; \
- mov eax, [ecx] ; \
- call [eax+funcOffset] ; \
- }
-
-
-#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 )\
- void *this_ = (thisPtr); \
- __asm { \
- mov eax, param1 ; \
- push eax ; \
- mov ecx, this_ ; \
- mov eax, [ecx] ; \
- call [eax+funcOffset] ; \
- mov resultName, eax ; \
- }
-
-
-#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 )\
- void *this_ = (thisPtr); \
- void *doubleParamPtr_ (¶m1); \
- __asm { \
- mov eax, doubleParamPtr_ ; \
- push [eax+4] ; \
- push [eax] ; \
- mov ecx, this_ ; \
- mov eax, [ecx] ; \
- call [eax+funcOffset] ; \
- mov resultName, eax ; \
- }
-
-
-#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 )\
- void *this_ = (thisPtr); \
- __asm { \
- mov eax, param2 ; \
- push eax ; \
- mov eax, param1 ; \
- push eax ; \
- mov ecx, this_ ; \
- mov eax, [ecx] ; \
- call [eax+funcOffset] ; \
- mov resultName, eax ; \
- }
-
-
-#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\
- void *this_ = (thisPtr); \
- __asm { \
- mov eax, param4 ; \
- push eax ; \
- mov eax, param3 ; \
- push eax ; \
- mov eax, param2 ; \
- push eax ; \
- mov eax, param1 ; \
- push eax ; \
- mov ecx, this_ ; \
- mov eax, [ecx] ; \
- call [eax+funcOffset] ; \
- mov resultName, eax ; \
- }
-
-
-#elif defined(__GNUC__)
-
-
-#define CALL_THISCALL_0( resultName, thisPtr, funcOffset ) \
- __asm__ __volatile__ ("movl (%1), %%edx\n\t" \
- "call *"#funcOffset"(%%edx)\n\t" \
- :"=a"(resultName) /* Output Operands */ \
- :"c"(thisPtr) /* Input Operands */ \
- : "%edx" /* Clobbered Registers */ \
- ); \
-
-
-#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 ) \
- __asm__ __volatile__ ("pushl %0\n\t" \
- "movl (%1), %%edx\n\t" \
- "call *"#funcOffset"(%%edx)\n\t" \
- : /* Output Operands */ \
- :"r"(param1), /* Input Operands */ \
- "c"(thisPtr) \
- : "%edx" /* Clobbered Registers */ \
- ); \
-
-
-#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 ) \
- __asm__ __volatile__ ("pushl %1\n\t" \
- "movl (%2), %%edx\n\t" \
- "call *"#funcOffset"(%%edx)\n\t" \
- :"=a"(resultName) /* Output Operands */ \
- :"r"(param1), /* Input Operands */ \
- "c"(thisPtr) \
- : "%edx" /* Clobbered Registers */ \
- ); \
-
-
-#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 ) \
- do { \
- double param1f64 = param1; /* Cast explicitly to double */ \
- double *param1f64Ptr = ¶m1f64; /* Make pointer to address */ \
- __asm__ __volatile__ ("pushl 4(%1)\n\t" \
- "pushl (%1)\n\t" \
- "movl (%2), %%edx\n\t" \
- "call *"#funcOffset"(%%edx);\n\t" \
- : "=a"(resultName) /* Output Operands */ \
- : "r"(param1f64Ptr), /* Input Operands */ \
- "c"(thisPtr), \
- "m"(*param1f64Ptr) /* Using address */ \
- : "%edx" /* Clobbered Registers */ \
- ); \
- } while (0); \
-
-
-#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 ) \
- __asm__ __volatile__ ("pushl %1\n\t" \
- "pushl %2\n\t" \
- "movl (%3), %%edx\n\t" \
- "call *"#funcOffset"(%%edx)\n\t" \
- :"=a"(resultName) /* Output Operands */ \
- :"r"(param2), /* Input Operands */ \
- "r"(param1), \
- "c"(thisPtr) \
- : "%edx" /* Clobbered Registers */ \
- ); \
-
-
-#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\
- __asm__ __volatile__ ("pushl %1\n\t" \
- "pushl %2\n\t" \
- "pushl %3\n\t" \
- "pushl %4\n\t" \
- "movl (%5), %%edx\n\t" \
- "call *"#funcOffset"(%%edx)\n\t" \
- :"=a"(resultName) /* Output Operands */ \
- :"r"(param4), /* Input Operands */ \
- "r"(param3), \
- "r"(param2), \
- "r"(param1), \
- "c"(thisPtr) \
- : "%edx" /* Clobbered Registers */ \
- ); \
-
-#endif
-
-
-
-// Our static singleton instance.
-IASIOThiscallResolver IASIOThiscallResolver::instance;
-
-// Constructor called to initialize static Singleton instance above. Note that
-// it is important not to clear that_ incase it has already been set by the call
-// to placement new in ASIOInit().
-IASIOThiscallResolver::IASIOThiscallResolver()
-{
-}
-
-// Constructor called from ASIOInit() below
-IASIOThiscallResolver::IASIOThiscallResolver(IASIO* that)
-: that_( that )
-{
-}
-
-// Implement IUnknown methods as assert(false). IASIOThiscallResolver is not
-// really a COM object, just a wrapper which will work with the ASIO SDK.
-// If you wanted to use ASIO without the SDK you might want to implement COM
-// aggregation in these methods.
-HRESULT STDMETHODCALLTYPE IASIOThiscallResolver::QueryInterface(REFIID riid, void **ppv)
-{
- (void)riid; // suppress unused variable warning
-
- assert( false ); // this function should never be called by the ASIO SDK.
-
- *ppv = NULL;
- return E_NOINTERFACE;
-}
-
-ULONG STDMETHODCALLTYPE IASIOThiscallResolver::AddRef()
-{
- assert( false ); // this function should never be called by the ASIO SDK.
-
- return 1;
-}
-
-ULONG STDMETHODCALLTYPE IASIOThiscallResolver::Release()
-{
- assert( false ); // this function should never be called by the ASIO SDK.
-
- return 1;
-}
-
-
-// Implement the IASIO interface methods by performing the vptr manipulation
-// described above then delegating to the real implementation.
-ASIOBool IASIOThiscallResolver::init(void *sysHandle)
-{
- ASIOBool result;
- CALL_THISCALL_1( result, that_, 12, sysHandle );
- return result;
-}
-
-void IASIOThiscallResolver::getDriverName(char *name)
-{
- CALL_VOID_THISCALL_1( that_, 16, name );
-}
-
-long IASIOThiscallResolver::getDriverVersion()
-{
- ASIOBool result;
- CALL_THISCALL_0( result, that_, 20 );
- return result;
-}
-
-void IASIOThiscallResolver::getErrorMessage(char *string)
-{
- CALL_VOID_THISCALL_1( that_, 24, string );
-}
-
-ASIOError IASIOThiscallResolver::start()
-{
- ASIOBool result;
- CALL_THISCALL_0( result, that_, 28 );
- return result;
-}
-
-ASIOError IASIOThiscallResolver::stop()
-{
- ASIOBool result;
- CALL_THISCALL_0( result, that_, 32 );
- return result;
-}
-
-ASIOError IASIOThiscallResolver::getChannels(long *numInputChannels, long *numOutputChannels)
-{
- ASIOBool result;
- CALL_THISCALL_2( result, that_, 36, numInputChannels, numOutputChannels );
- return result;
-}
-
-ASIOError IASIOThiscallResolver::getLatencies(long *inputLatency, long *outputLatency)
-{
- ASIOBool result;
- CALL_THISCALL_2( result, that_, 40, inputLatency, outputLatency );
- return result;
-}
-
-ASIOError IASIOThiscallResolver::getBufferSize(long *minSize, long *maxSize,
- long *preferredSize, long *granularity)
-{
- ASIOBool result;
- CALL_THISCALL_4( result, that_, 44, minSize, maxSize, preferredSize, granularity );
- return result;
-}
-
-ASIOError IASIOThiscallResolver::canSampleRate(ASIOSampleRate sampleRate)
-{
- ASIOBool result;
- CALL_THISCALL_1_DOUBLE( result, that_, 48, sampleRate );
- return result;
-}
-
-ASIOError IASIOThiscallResolver::getSampleRate(ASIOSampleRate *sampleRate)
-{
- ASIOBool result;
- CALL_THISCALL_1( result, that_, 52, sampleRate );
- return result;
-}
-
-ASIOError IASIOThiscallResolver::setSampleRate(ASIOSampleRate sampleRate)
-{
- ASIOBool result;
- CALL_THISCALL_1_DOUBLE( result, that_, 56, sampleRate );
- return result;
-}
-
-ASIOError IASIOThiscallResolver::getClockSources(ASIOClockSource *clocks, long *numSources)
-{
- ASIOBool result;
- CALL_THISCALL_2( result, that_, 60, clocks, numSources );
- return result;
-}
-
-ASIOError IASIOThiscallResolver::setClockSource(long reference)
-{
- ASIOBool result;
- CALL_THISCALL_1( result, that_, 64, reference );
- return result;
-}
-
-ASIOError IASIOThiscallResolver::getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp)
-{
- ASIOBool result;
- CALL_THISCALL_2( result, that_, 68, sPos, tStamp );
- return result;
-}
-
-ASIOError IASIOThiscallResolver::getChannelInfo(ASIOChannelInfo *info)
-{
- ASIOBool result;
- CALL_THISCALL_1( result, that_, 72, info );
- return result;
-}
-
-ASIOError IASIOThiscallResolver::createBuffers(ASIOBufferInfo *bufferInfos,
- long numChannels, long bufferSize, ASIOCallbacks *callbacks)
-{
- ASIOBool result;
- CALL_THISCALL_4( result, that_, 76, bufferInfos, numChannels, bufferSize, callbacks );
- return result;
-}
-
-ASIOError IASIOThiscallResolver::disposeBuffers()
-{
- ASIOBool result;
- CALL_THISCALL_0( result, that_, 80 );
- return result;
-}
-
-ASIOError IASIOThiscallResolver::controlPanel()
-{
- ASIOBool result;
- CALL_THISCALL_0( result, that_, 84 );
- return result;
-}
-
-ASIOError IASIOThiscallResolver::future(long selector,void *opt)
-{
- ASIOBool result;
- CALL_THISCALL_2( result, that_, 88, selector, opt );
- return result;
-}
-
-ASIOError IASIOThiscallResolver::outputReady()
-{
- ASIOBool result;
- CALL_THISCALL_0( result, that_, 92 );
- return result;
-}
-
-
-// Implement our substitute ASIOInit() method
-ASIOError IASIOThiscallResolver::ASIOInit(ASIODriverInfo *info)
-{
- // To ensure that our instance's vptr is correctly constructed, even if
- // ASIOInit is called prior to main(), we explicitly call its constructor
- // (potentially over the top of an existing instance). Note that this is
- // pretty ugly, and is only safe because IASIOThiscallResolver has no
- // destructor and contains no objects with destructors.
- new((void*)&instance) IASIOThiscallResolver( theAsioDriver );
-
- // Interpose between ASIO client code and the real driver.
- theAsioDriver = &instance;
-
- // Note that we never need to switch theAsioDriver back to point to the
- // real driver because theAsioDriver is reset to zero in ASIOExit().
-
- // Delegate to the real ASIOInit
- return ::ASIOInit(info);
-}
-
-
-#endif /* !defined(_MSC_VER) */
-
-#endif /* Win32 */
-
+++ /dev/null
-// ****************************************************************************\r
-//\r
-// Changed: I have modified this file slightly (includes) to work with\r
-// RtAudio. RtAudio.cpp must include this file after asio.h. \r
-//\r
-// File: IASIOThiscallResolver.h\r
-// Description: The IASIOThiscallResolver class implements the IASIO\r
-// interface and acts as a proxy to the real IASIO interface by\r
-// calling through its vptr table using the thiscall calling\r
-// convention. To put it another way, we interpose\r
-// IASIOThiscallResolver between ASIO SDK code and the driver.\r
-// This is necessary because most non-Microsoft compilers don't\r
-// implement the thiscall calling convention used by IASIO.\r
-//\r
-// iasiothiscallresolver.cpp contains the background of this\r
-// problem plus a technical description of the vptr\r
-// manipulations.\r
-//\r
-// In order to use this mechanism one simply has to add\r
-// iasiothiscallresolver.cpp to the list of files to compile\r
-// and #include <iasiothiscallresolver.h>\r
-//\r
-// Note that this #include must come after the other ASIO SDK\r
-// #includes, for example:\r
-//\r
-// #include <windows.h>\r
-// #include <asiosys.h>\r
-// #include <asio.h>\r
-// #include <asiodrivers.h>\r
-// #include <iasiothiscallresolver.h>\r
-//\r
-// Actually the important thing is to #include\r
-// <iasiothiscallresolver.h> after <asio.h>. We have\r
-// incorporated a test to enforce this ordering.\r
-//\r
-// The code transparently takes care of the interposition by\r
-// using macro substitution to intercept calls to ASIOInit()\r
-// and ASIOExit(). We save the original ASIO global\r
-// "theAsioDriver" in our "that" variable, and then set\r
-// "theAsioDriver" to equal our IASIOThiscallResolver instance.\r
-//\r
-// Whilst this method of resolving the thiscall problem requires\r
-// the addition of #include <iasiothiscallresolver.h> to client\r
-// code it has the advantage that it does not break the terms\r
-// of the ASIO licence by publishing it. We are NOT modifying\r
-// any Steinberg code here, we are merely implementing the IASIO\r
-// interface in the same way that we would need to do if we\r
-// wished to provide an open source ASIO driver.\r
-//\r
-// For compilation with MinGW -lole32 needs to be added to the\r
-// linker options. For BORLAND, linking with Import32.lib is\r
-// sufficient.\r
-//\r
-// The dependencies are with: CoInitialize, CoUninitialize,\r
-// CoCreateInstance, CLSIDFromString - used by asiolist.cpp\r
-// and are required on Windows whether ThiscallResolver is used\r
-// or not.\r
-//\r
-// Searching for the above strings in the root library path\r
-// of your compiler should enable the correct libraries to be\r
-// identified if they aren't immediately obvious.\r
-//\r
-// Note that the current implementation of IASIOThiscallResolver\r
-// is not COM compliant - it does not correctly implement the\r
-// IUnknown interface. Implementing it is not necessary because\r
-// it is not called by parts of the ASIO SDK which call through\r
-// theAsioDriver ptr. The IUnknown methods are implemented as\r
-// assert(false) to ensure that the code fails if they are\r
-// ever called.\r
-// Restrictions: None. Public Domain & Open Source distribute freely\r
-// You may use IASIOThiscallResolver commercially as well as\r
-// privately.\r
-// You the user assume the responsibility for the use of the\r
-// files, binary or text, and there is no guarantee or warranty,\r
-// expressed or implied, including but not limited to the\r
-// implied warranties of merchantability and fitness for a\r
-// particular purpose. You assume all responsibility and agree\r
-// to hold no entity, copyright holder or distributors liable\r
-// for any loss of data or inaccurate representations of data\r
-// as a result of using IASIOThiscallResolver.\r
-// Version: 1.4 Added separate macro CALL_THISCALL_1_DOUBLE from\r
-// Andrew Baldwin, and volatile for whole gcc asm blocks,\r
-// both for compatibility with newer gcc versions. Cleaned up\r
-// Borland asm to use one less register.\r
-// 1.3 Switched to including assert.h for better compatibility.\r
-// Wrapped entire .h and .cpp contents with a check for\r
-// _MSC_VER to provide better compatibility with MS compilers.\r
-// Changed Singleton implementation to use static instance\r
-// instead of freestore allocated instance. Removed ASIOExit\r
-// macro as it is no longer needed.\r
-// 1.2 Removed semicolons from ASIOInit and ASIOExit macros to\r
-// allow them to be embedded in expressions (if statements).\r
-// Cleaned up some comments. Removed combase.c dependency (it\r
-// doesn't compile with BCB anyway) by stubbing IUnknown.\r
-// 1.1 Incorporated comments from Ross Bencina including things\r
-// such as changing name from ThiscallResolver to\r
-// IASIOThiscallResolver, tidying up the constructor, fixing\r
-// a bug in IASIOThiscallResolver::ASIOExit() and improving\r
-// portability through the use of conditional compilation\r
-// 1.0 Initial working version.\r
-// Created: 6/09/2003\r
-// Authors: Fraser Adams\r
-// Ross Bencina\r
-// Rene G. Ceballos\r
-// Martin Fay\r
-// Antti Silvast\r
-// Andrew Baldwin\r
-//\r
-// ****************************************************************************\r
-\r
-\r
-#ifndef included_iasiothiscallresolver_h\r
-#define included_iasiothiscallresolver_h\r
-\r
-// We only need IASIOThiscallResolver at all if we are on Win32. For other\r
-// platforms we simply bypass the IASIOThiscallResolver definition to allow us\r
-// to be safely #include'd whatever the platform to keep client code portable\r
-//#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)\r
-#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(_WIN64)\r
-\r
-\r
-// If microsoft compiler we can call IASIO directly so IASIOThiscallResolver\r
-// is not used.\r
-#if !defined(_MSC_VER)\r
-\r
-\r
-// The following is in order to ensure that this header is only included after\r
-// the other ASIO headers (except for the case of iasiothiscallresolver.cpp).\r
-// We need to do this because IASIOThiscallResolver works by eclipsing the\r
-// original definition of ASIOInit() with a macro (see below).\r
-#if !defined(iasiothiscallresolver_sourcefile)\r
- #if !defined(__ASIO_H)\r
- #error iasiothiscallresolver.h must be included AFTER asio.h\r
- #endif\r
-#endif\r
-\r
-#include <windows.h>\r
-#include "iasiodrv.h" /* From ASIO SDK */\r
-\r
-\r
-class IASIOThiscallResolver : public IASIO {\r
-private:\r
- IASIO* that_; // Points to the real IASIO\r
-\r
- static IASIOThiscallResolver instance; // Singleton instance\r
-\r
- // Constructors - declared private so construction is limited to\r
- // our Singleton instance\r
- IASIOThiscallResolver();\r
- IASIOThiscallResolver(IASIO* that);\r
-public:\r
-\r
- // Methods from the IUnknown interface. We don't fully implement IUnknown\r
- // because the ASIO SDK never calls these methods through theAsioDriver ptr.\r
- // These methods are implemented as assert(false).\r
- virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv);\r
- virtual ULONG STDMETHODCALLTYPE AddRef();\r
- virtual ULONG STDMETHODCALLTYPE Release();\r
-\r
- // Methods from the IASIO interface, implemented as forwarning calls to that.\r
- virtual ASIOBool init(void *sysHandle);\r
- virtual void getDriverName(char *name);\r
- virtual long getDriverVersion();\r
- virtual void getErrorMessage(char *string);\r
- virtual ASIOError start();\r
- virtual ASIOError stop();\r
- virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels);\r
- virtual ASIOError getLatencies(long *inputLatency, long *outputLatency);\r
- virtual ASIOError getBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity);\r
- virtual ASIOError canSampleRate(ASIOSampleRate sampleRate);\r
- virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate);\r
- virtual ASIOError setSampleRate(ASIOSampleRate sampleRate);\r
- virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources);\r
- virtual ASIOError setClockSource(long reference);\r
- virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp);\r
- virtual ASIOError getChannelInfo(ASIOChannelInfo *info);\r
- virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks);\r
- virtual ASIOError disposeBuffers();\r
- virtual ASIOError controlPanel();\r
- virtual ASIOError future(long selector,void *opt);\r
- virtual ASIOError outputReady();\r
-\r
- // Class method, see ASIOInit() macro below.\r
- static ASIOError ASIOInit(ASIODriverInfo *info); // Delegates to ::ASIOInit\r
-};\r
-\r
-\r
-// Replace calls to ASIOInit with our interposing version.\r
-// This macro enables us to perform thiscall resolution simply by #including\r
-// <iasiothiscallresolver.h> after the asio #includes (this file _must_ be\r
-// included _after_ the asio #includes)\r
-\r
-#define ASIOInit(name) IASIOThiscallResolver::ASIOInit((name))\r
-\r
-\r
-#endif /* !defined(_MSC_VER) */\r
-\r
-#endif /* Win32 */\r
-\r
-#endif /* included_iasiothiscallresolver_h */\r
-\r
-\r
+++ /dev/null
-/*
- * soundcard.h
- */
-
-/*-
- * Copyright by Hannu Savolainen 1993 / 4Front Technologies 1993-2006
- * Modified for the new FreeBSD sound driver by Luigi Rizzo, 1997
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * $FreeBSD: src/sys/sys/soundcard.h,v 1.48 2006/11/26 11:55:48 netchild Exp $
- */
-
-/*
- * Unless coordinating changes with 4Front Technologies, do NOT make any
- * modifications to ioctl commands, types, etc. that would break
- * compatibility with the OSS API.
- */
-
-#ifndef _SYS_SOUNDCARD_H_
-#define _SYS_SOUNDCARD_H_
- /*
- * If you make modifications to this file, please contact me before
- * distributing the modified version. There is already enough
- * diversity in the world.
- *
- * Regards,
- * Hannu Savolainen
- * hannu@voxware.pp.fi
- *
- **********************************************************************
- * PS. The Hacker's Guide to VoxWare available from
- * nic.funet.fi:pub/Linux/ALPHA/sound. The file is
- * snd-sdk-doc-0.1.ps.gz (gzipped postscript). It contains
- * some useful information about programming with VoxWare.
- * (NOTE! The pub/Linux/ALPHA/ directories are hidden. You have
- * to cd inside them before the files are accessible.)
- **********************************************************************
- */
-
-/*
- * SOUND_VERSION is only used by the voxware driver. Hopefully apps
- * should not depend on it, but rather look at the capabilities
- * of the driver in the kernel!
- */
-#define SOUND_VERSION 301
-#define VOXWARE /* does this have any use ? */
-
-/*
- * Supported card ID numbers (Should be somewhere else? We keep
- * them here just for compativility with the old driver, but these
- * constants are of little or no use).
- */
-
-#define SNDCARD_ADLIB 1
-#define SNDCARD_SB 2
-#define SNDCARD_PAS 3
-#define SNDCARD_GUS 4
-#define SNDCARD_MPU401 5
-#define SNDCARD_SB16 6
-#define SNDCARD_SB16MIDI 7
-#define SNDCARD_UART6850 8
-#define SNDCARD_GUS16 9
-#define SNDCARD_MSS 10
-#define SNDCARD_PSS 11
-#define SNDCARD_SSCAPE 12
-#define SNDCARD_PSS_MPU 13
-#define SNDCARD_PSS_MSS 14
-#define SNDCARD_SSCAPE_MSS 15
-#define SNDCARD_TRXPRO 16
-#define SNDCARD_TRXPRO_SB 17
-#define SNDCARD_TRXPRO_MPU 18
-#define SNDCARD_MAD16 19
-#define SNDCARD_MAD16_MPU 20
-#define SNDCARD_CS4232 21
-#define SNDCARD_CS4232_MPU 22
-#define SNDCARD_MAUI 23
-#define SNDCARD_PSEUDO_MSS 24
-#define SNDCARD_AWE32 25
-#define SNDCARD_NSS 26
-#define SNDCARD_UART16550 27
-#define SNDCARD_OPL 28
-
-#include <sys/types.h>
-#include <machine/endian.h>
-#ifndef _IOWR
-#include <sys/ioccom.h>
-#endif /* !_IOWR */
-
-/*
- * The first part of this file contains the new FreeBSD sound ioctl
- * interface. Tries to minimize the number of different ioctls, and
- * to be reasonably general.
- *
- * 970821: some of the new calls have not been implemented yet.
- */
-
-/*
- * the following three calls extend the generic file descriptor
- * interface. AIONWRITE is the dual of FIONREAD, i.e. returns the max
- * number of bytes for a write operation to be non-blocking.
- *
- * AIOGSIZE/AIOSSIZE are used to change the behaviour of the device,
- * from a character device (default) to a block device. In block mode,
- * (not to be confused with blocking mode) the main difference for the
- * application is that select() will return only when a complete
- * block can be read/written to the device, whereas in character mode
- * select will return true when one byte can be exchanged. For audio
- * devices, character mode makes select almost useless since one byte
- * will always be ready by the next sample time (which is often only a
- * handful of microseconds away).
- * Use a size of 0 or 1 to return to character mode.
- */
-#define AIONWRITE _IOR('A', 10, int) /* get # bytes to write */
-struct snd_size {
- int play_size;
- int rec_size;
-};
-#define AIOGSIZE _IOR('A', 11, struct snd_size)/* read current blocksize */
-#define AIOSSIZE _IOWR('A', 11, struct snd_size) /* sets blocksize */
-
-/*
- * The following constants define supported audio formats. The
- * encoding follows voxware conventions, i.e. 1 bit for each supported
- * format. We extend it by using bit 31 (RO) to indicate full-duplex
- * capability, and bit 29 (RO) to indicate that the card supports/
- * needs different formats on capture & playback channels.
- * Bit 29 (RW) is used to indicate/ask stereo.
- *
- * The number of bits required to store the sample is:
- * o 4 bits for the IDA ADPCM format,
- * o 8 bits for 8-bit formats, mu-law and A-law,
- * o 16 bits for the 16-bit formats, and
- * o 32 bits for the 24/32-bit formats.
- * o undefined for the MPEG audio format.
- */
-
-#define AFMT_QUERY 0x00000000 /* Return current format */
-#define AFMT_MU_LAW 0x00000001 /* Logarithmic mu-law */
-#define AFMT_A_LAW 0x00000002 /* Logarithmic A-law */
-#define AFMT_IMA_ADPCM 0x00000004 /* A 4:1 compressed format where 16-bit
- * squence represented using the
- * the average 4 bits per sample */
-#define AFMT_U8 0x00000008 /* Unsigned 8-bit */
-#define AFMT_S16_LE 0x00000010 /* Little endian signed 16-bit */
-#define AFMT_S16_BE 0x00000020 /* Big endian signed 16-bit */
-#define AFMT_S8 0x00000040 /* Signed 8-bit */
-#define AFMT_U16_LE 0x00000080 /* Little endian unsigned 16-bit */
-#define AFMT_U16_BE 0x00000100 /* Big endian unsigned 16-bit */
-#define AFMT_MPEG 0x00000200 /* MPEG MP2/MP3 audio */
-#define AFMT_AC3 0x00000400 /* Dolby Digital AC3 */
-
-#if _BYTE_ORDER == _LITTLE_ENDIAN
-#define AFMT_S16_NE AFMT_S16_LE /* native endian signed 16 */
-#else
-#define AFMT_S16_NE AFMT_S16_BE
-#endif
-
-/*
- * 32-bit formats below used for 24-bit audio data where the data is stored
- * in the 24 most significant bits and the least significant bits are not used
- * (should be set to 0).
- */
-#define AFMT_S32_LE 0x00001000 /* Little endian signed 32-bit */
-#define AFMT_S32_BE 0x00002000 /* Big endian signed 32-bit */
-#define AFMT_U32_LE 0x00004000 /* Little endian unsigned 32-bit */
-#define AFMT_U32_BE 0x00008000 /* Big endian unsigned 32-bit */
-#define AFMT_S24_LE 0x00010000 /* Little endian signed 24-bit */
-#define AFMT_S24_BE 0x00020000 /* Big endian signed 24-bit */
-#define AFMT_U24_LE 0x00040000 /* Little endian unsigned 24-bit */
-#define AFMT_U24_BE 0x00080000 /* Big endian unsigned 24-bit */
-
-#define AFMT_STEREO 0x10000000 /* can do/want stereo */
-
-/*
- * the following are really capabilities
- */
-#define AFMT_WEIRD 0x20000000 /* weird hardware... */
- /*
- * AFMT_WEIRD reports that the hardware might need to operate
- * with different formats in the playback and capture
- * channels when operating in full duplex.
- * As an example, SoundBlaster16 cards only support U8 in one
- * direction and S16 in the other one, and applications should
- * be aware of this limitation.
- */
-#define AFMT_FULLDUPLEX 0x80000000 /* can do full duplex */
-
-/*
- * The following structure is used to get/set format and sampling rate.
- * While it would be better to have things such as stereo, bits per
- * sample, endiannes, etc split in different variables, it turns out
- * that formats are not that many, and not all combinations are possible.
- * So we followed the Voxware approach of associating one bit to each
- * format.
- */
-
-typedef struct _snd_chan_param {
- u_long play_rate; /* sampling rate */
- u_long rec_rate; /* sampling rate */
- u_long play_format; /* everything describing the format */
- u_long rec_format; /* everything describing the format */
-} snd_chan_param;
-#define AIOGFMT _IOR('f', 12, snd_chan_param) /* get format */
-#define AIOSFMT _IOWR('f', 12, snd_chan_param) /* sets format */
-
-/*
- * The following structure is used to get/set the mixer setting.
- * Up to 32 mixers are supported, each one with up to 32 channels.
- */
-typedef struct _snd_mix_param {
- u_char subdev; /* which output */
- u_char line; /* which input */
- u_char left,right; /* volumes, 0..255, 0 = mute */
-} snd_mix_param ;
-
-/* XXX AIOGMIX, AIOSMIX not implemented yet */
-#define AIOGMIX _IOWR('A', 13, snd_mix_param) /* return mixer status */
-#define AIOSMIX _IOWR('A', 14, snd_mix_param) /* sets mixer status */
-
-/*
- * channel specifiers used in AIOSTOP and AIOSYNC
- */
-#define AIOSYNC_PLAY 0x1 /* play chan */
-#define AIOSYNC_CAPTURE 0x2 /* capture chan */
-/* AIOSTOP stop & flush a channel, returns the residual count */
-#define AIOSTOP _IOWR ('A', 15, int)
-
-/* alternate method used to notify the sync condition */
-#define AIOSYNC_SIGNAL 0x100
-#define AIOSYNC_SELECT 0x200
-
-/* what the 'pos' field refers to */
-#define AIOSYNC_READY 0x400
-#define AIOSYNC_FREE 0x800
-
-typedef struct _snd_sync_parm {
- long chan ; /* play or capture channel, plus modifier */
- long pos;
-} snd_sync_parm;
-#define AIOSYNC _IOWR ('A', 15, snd_sync_parm) /* misc. synchronization */
-
-/*
- * The following is used to return device capabilities. If the structure
- * passed to the ioctl is zeroed, default values are returned for rate
- * and formats, a bitmap of available mixers is returned, and values
- * (inputs, different levels) for the first one are returned.
- *
- * If formats, mixers, inputs are instantiated, then detailed info
- * are returned depending on the call.
- */
-typedef struct _snd_capabilities {
- u_long rate_min, rate_max; /* min-max sampling rate */
- u_long formats;
- u_long bufsize; /* DMA buffer size */
- u_long mixers; /* bitmap of available mixers */
- u_long inputs; /* bitmap of available inputs (per mixer) */
- u_short left, right; /* how many levels are supported */
-} snd_capabilities;
-#define AIOGCAP _IOWR('A', 15, snd_capabilities) /* get capabilities */
-
-/*
- * here is the old (Voxware) ioctl interface
- */
-
-/*
- * IOCTL Commands for /dev/sequencer
- */
-
-#define SNDCTL_SEQ_RESET _IO ('Q', 0)
-#define SNDCTL_SEQ_SYNC _IO ('Q', 1)
-#define SNDCTL_SYNTH_INFO _IOWR('Q', 2, struct synth_info)
-#define SNDCTL_SEQ_CTRLRATE _IOWR('Q', 3, int) /* Set/get timer res.(hz) */
-#define SNDCTL_SEQ_GETOUTCOUNT _IOR ('Q', 4, int)
-#define SNDCTL_SEQ_GETINCOUNT _IOR ('Q', 5, int)
-#define SNDCTL_SEQ_PERCMODE _IOW ('Q', 6, int)
-#define SNDCTL_FM_LOAD_INSTR _IOW ('Q', 7, struct sbi_instrument) /* Valid for FM only */
-#define SNDCTL_SEQ_TESTMIDI _IOW ('Q', 8, int)
-#define SNDCTL_SEQ_RESETSAMPLES _IOW ('Q', 9, int)
-#define SNDCTL_SEQ_NRSYNTHS _IOR ('Q',10, int)
-#define SNDCTL_SEQ_NRMIDIS _IOR ('Q',11, int)
-#define SNDCTL_MIDI_INFO _IOWR('Q',12, struct midi_info)
-#define SNDCTL_SEQ_THRESHOLD _IOW ('Q',13, int)
-#define SNDCTL_SEQ_TRESHOLD SNDCTL_SEQ_THRESHOLD /* there was once a typo */
-#define SNDCTL_SYNTH_MEMAVL _IOWR('Q',14, int) /* in=dev#, out=memsize */
-#define SNDCTL_FM_4OP_ENABLE _IOW ('Q',15, int) /* in=dev# */
-#define SNDCTL_PMGR_ACCESS _IOWR('Q',16, struct patmgr_info)
-#define SNDCTL_SEQ_PANIC _IO ('Q',17)
-#define SNDCTL_SEQ_OUTOFBAND _IOW ('Q',18, struct seq_event_rec)
-#define SNDCTL_SEQ_GETTIME _IOR ('Q',19, int)
-
-struct seq_event_rec {
- u_char arr[8];
-};
-
-#define SNDCTL_TMR_TIMEBASE _IOWR('T', 1, int)
-#define SNDCTL_TMR_START _IO ('T', 2)
-#define SNDCTL_TMR_STOP _IO ('T', 3)
-#define SNDCTL_TMR_CONTINUE _IO ('T', 4)
-#define SNDCTL_TMR_TEMPO _IOWR('T', 5, int)
-#define SNDCTL_TMR_SOURCE _IOWR('T', 6, int)
-# define TMR_INTERNAL 0x00000001
-# define TMR_EXTERNAL 0x00000002
-# define TMR_MODE_MIDI 0x00000010
-# define TMR_MODE_FSK 0x00000020
-# define TMR_MODE_CLS 0x00000040
-# define TMR_MODE_SMPTE 0x00000080
-#define SNDCTL_TMR_METRONOME _IOW ('T', 7, int)
-#define SNDCTL_TMR_SELECT _IOW ('T', 8, int)
-
-/*
- * Endian aware patch key generation algorithm.
- */
-
-#if defined(_AIX) || defined(AIX)
-# define _PATCHKEY(id) (0xfd00|id)
-#else
-# define _PATCHKEY(id) ((id<<8)|0xfd)
-#endif
-
-/*
- * Sample loading mechanism for internal synthesizers (/dev/sequencer)
- * The following patch_info structure has been designed to support
- * Gravis UltraSound. It tries to be universal format for uploading
- * sample based patches but is probably too limited.
- */
-
-struct patch_info {
-/* u_short key; Use GUS_PATCH here */
- short key; /* Use GUS_PATCH here */
-#define GUS_PATCH _PATCHKEY(0x04)
-#define OBSOLETE_GUS_PATCH _PATCHKEY(0x02)
-
- short device_no; /* Synthesizer number */
- short instr_no; /* Midi pgm# */
-
- u_long mode;
-/*
- * The least significant byte has the same format than the GUS .PAT
- * files
- */
-#define WAVE_16_BITS 0x01 /* bit 0 = 8 or 16 bit wave data. */
-#define WAVE_UNSIGNED 0x02 /* bit 1 = Signed - Unsigned data. */
-#define WAVE_LOOPING 0x04 /* bit 2 = looping enabled-1. */
-#define WAVE_BIDIR_LOOP 0x08 /* bit 3 = Set is bidirectional looping. */
-#define WAVE_LOOP_BACK 0x10 /* bit 4 = Set is looping backward. */
-#define WAVE_SUSTAIN_ON 0x20 /* bit 5 = Turn sustaining on. (Env. pts. 3)*/
-#define WAVE_ENVELOPES 0x40 /* bit 6 = Enable envelopes - 1 */
- /* (use the env_rate/env_offs fields). */
-/* Linux specific bits */
-#define WAVE_VIBRATO 0x00010000 /* The vibrato info is valid */
-#define WAVE_TREMOLO 0x00020000 /* The tremolo info is valid */
-#define WAVE_SCALE 0x00040000 /* The scaling info is valid */
-/* Other bits must be zeroed */
-
- long len; /* Size of the wave data in bytes */
- long loop_start, loop_end; /* Byte offsets from the beginning */
-
-/*
- * The base_freq and base_note fields are used when computing the
- * playback speed for a note. The base_note defines the tone frequency
- * which is heard if the sample is played using the base_freq as the
- * playback speed.
- *
- * The low_note and high_note fields define the minimum and maximum note
- * frequencies for which this sample is valid. It is possible to define
- * more than one samples for an instrument number at the same time. The
- * low_note and high_note fields are used to select the most suitable one.
- *
- * The fields base_note, high_note and low_note should contain
- * the note frequency multiplied by 1000. For example value for the
- * middle A is 440*1000.
- */
-
- u_int base_freq;
- u_long base_note;
- u_long high_note;
- u_long low_note;
- int panning; /* -128=left, 127=right */
- int detuning;
-
-/* New fields introduced in version 1.99.5 */
-
- /* Envelope. Enabled by mode bit WAVE_ENVELOPES */
- u_char env_rate[ 6 ]; /* GUS HW ramping rate */
- u_char env_offset[ 6 ]; /* 255 == 100% */
-
- /*
- * The tremolo, vibrato and scale info are not supported yet.
- * Enable by setting the mode bits WAVE_TREMOLO, WAVE_VIBRATO or
- * WAVE_SCALE
- */
-
- u_char tremolo_sweep;
- u_char tremolo_rate;
- u_char tremolo_depth;
-
- u_char vibrato_sweep;
- u_char vibrato_rate;
- u_char vibrato_depth;
-
- int scale_frequency;
- u_int scale_factor; /* from 0 to 2048 or 0 to 2 */
-
- int volume;
- int spare[4];
- char data[1]; /* The waveform data starts here */
-};
-
-struct sysex_info {
- short key; /* Use GUS_PATCH here */
-#define SYSEX_PATCH _PATCHKEY(0x05)
-#define MAUI_PATCH _PATCHKEY(0x06)
- short device_no; /* Synthesizer number */
- long len; /* Size of the sysex data in bytes */
- u_char data[1]; /* Sysex data starts here */
-};
-
-/*
- * Patch management interface (/dev/sequencer, /dev/patmgr#)
- * Don't use these calls if you want to maintain compatibility with
- * the future versions of the driver.
- */
-
-#define PS_NO_PATCHES 0 /* No patch support on device */
-#define PS_MGR_NOT_OK 1 /* Plain patch support (no mgr) */
-#define PS_MGR_OK 2 /* Patch manager supported */
-#define PS_MANAGED 3 /* Patch manager running */
-
-#define SNDCTL_PMGR_IFACE _IOWR('P', 1, struct patmgr_info)
-
-/*
- * The patmgr_info is a fixed size structure which is used for two
- * different purposes. The intended use is for communication between
- * the application using /dev/sequencer and the patch manager daemon
- * associated with a synthesizer device (ioctl(SNDCTL_PMGR_ACCESS)).
- *
- * This structure is also used with ioctl(SNDCTL_PGMR_IFACE) which allows
- * a patch manager daemon to read and write device parameters. This
- * ioctl available through /dev/sequencer also. Avoid using it since it's
- * extremely hardware dependent. In addition access trough /dev/sequencer
- * may confuse the patch manager daemon.
- */
-
-struct patmgr_info { /* Note! size must be < 4k since kmalloc() is used */
- u_long key; /* Don't worry. Reserved for communication
- between the patch manager and the driver. */
-#define PM_K_EVENT 1 /* Event from the /dev/sequencer driver */
-#define PM_K_COMMAND 2 /* Request from an application */
-#define PM_K_RESPONSE 3 /* From patmgr to application */
-#define PM_ERROR 4 /* Error returned by the patmgr */
- int device;
- int command;
-
-/*
- * Commands 0x000 to 0xfff reserved for patch manager programs
- */
-#define PM_GET_DEVTYPE 1 /* Returns type of the patch mgr interface of dev */
-#define PMTYPE_FM2 1 /* 2 OP fm */
-#define PMTYPE_FM4 2 /* Mixed 4 or 2 op FM (OPL-3) */
-#define PMTYPE_WAVE 3 /* Wave table synthesizer (GUS) */
-#define PM_GET_NRPGM 2 /* Returns max # of midi programs in parm1 */
-#define PM_GET_PGMMAP 3 /* Returns map of loaded midi programs in data8 */
-#define PM_GET_PGM_PATCHES 4 /* Return list of patches of a program (parm1) */
-#define PM_GET_PATCH 5 /* Return patch header of patch parm1 */
-#define PM_SET_PATCH 6 /* Set patch header of patch parm1 */
-#define PM_READ_PATCH 7 /* Read patch (wave) data */
-#define PM_WRITE_PATCH 8 /* Write patch (wave) data */
-
-/*
- * Commands 0x1000 to 0xffff are for communication between the patch manager
- * and the client
- */
-#define _PM_LOAD_PATCH 0x100
-
-/*
- * Commands above 0xffff reserved for device specific use
- */
-
- long parm1;
- long parm2;
- long parm3;
-
- union {
- u_char data8[4000];
- u_short data16[2000];
- u_long data32[1000];
- struct patch_info patch;
- } data;
-};
-
-/*
- * When a patch manager daemon is present, it will be informed by the
- * driver when something important happens. For example when the
- * /dev/sequencer is opened or closed. A record with key == PM_K_EVENT is
- * returned. The command field contains the event type:
- */
-#define PM_E_OPENED 1 /* /dev/sequencer opened */
-#define PM_E_CLOSED 2 /* /dev/sequencer closed */
-#define PM_E_PATCH_RESET 3 /* SNDCTL_RESETSAMPLES called */
-#define PM_E_PATCH_LOADED 4 /* A patch has been loaded by appl */
-
-/*
- * /dev/sequencer input events.
- *
- * The data written to the /dev/sequencer is a stream of events. Events
- * are records of 4 or 8 bytes. The first byte defines the size.
- * Any number of events can be written with a write call. There
- * is a set of macros for sending these events. Use these macros if you
- * want to maximize portability of your program.
- *
- * Events SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO. Are also input events.
- * (All input events are currently 4 bytes long. Be prepared to support
- * 8 byte events also. If you receive any event having first byte >= 128,
- * it's a 8 byte event.
- *
- * The events are documented at the end of this file.
- *
- * Normal events (4 bytes)
- * There is also a 8 byte version of most of the 4 byte events. The
- * 8 byte one is recommended.
- */
-#define SEQ_NOTEOFF 0
-#define SEQ_FMNOTEOFF SEQ_NOTEOFF /* Just old name */
-#define SEQ_NOTEON 1
-#define SEQ_FMNOTEON SEQ_NOTEON
-#define SEQ_WAIT TMR_WAIT_ABS
-#define SEQ_PGMCHANGE 3
-#define SEQ_FMPGMCHANGE SEQ_PGMCHANGE
-#define SEQ_SYNCTIMER TMR_START
-#define SEQ_MIDIPUTC 5
-#define SEQ_DRUMON 6 /*** OBSOLETE ***/
-#define SEQ_DRUMOFF 7 /*** OBSOLETE ***/
-#define SEQ_ECHO TMR_ECHO /* For synching programs with output */
-#define SEQ_AFTERTOUCH 9
-#define SEQ_CONTROLLER 10
-
-/*
- * Midi controller numbers
- *
- * Controllers 0 to 31 (0x00 to 0x1f) and 32 to 63 (0x20 to 0x3f)
- * are continuous controllers.
- * In the MIDI 1.0 these controllers are sent using two messages.
- * Controller numbers 0 to 31 are used to send the MSB and the
- * controller numbers 32 to 63 are for the LSB. Note that just 7 bits
- * are used in MIDI bytes.
- */
-
-#define CTL_BANK_SELECT 0x00
-#define CTL_MODWHEEL 0x01
-#define CTL_BREATH 0x02
-/* undefined 0x03 */
-#define CTL_FOOT 0x04
-#define CTL_PORTAMENTO_TIME 0x05
-#define CTL_DATA_ENTRY 0x06
-#define CTL_MAIN_VOLUME 0x07
-#define CTL_BALANCE 0x08
-/* undefined 0x09 */
-#define CTL_PAN 0x0a
-#define CTL_EXPRESSION 0x0b
-/* undefined 0x0c - 0x0f */
-#define CTL_GENERAL_PURPOSE1 0x10
-#define CTL_GENERAL_PURPOSE2 0x11
-#define CTL_GENERAL_PURPOSE3 0x12
-#define CTL_GENERAL_PURPOSE4 0x13
-/* undefined 0x14 - 0x1f */
-
-/* undefined 0x20 */
-
-/*
- * The controller numbers 0x21 to 0x3f are reserved for the
- * least significant bytes of the controllers 0x00 to 0x1f.
- * These controllers are not recognised by the driver.
- *
- * Controllers 64 to 69 (0x40 to 0x45) are on/off switches.
- * 0=OFF and 127=ON (intermediate values are possible)
- */
-#define CTL_DAMPER_PEDAL 0x40
-#define CTL_SUSTAIN CTL_DAMPER_PEDAL /* Alias */
-#define CTL_HOLD CTL_DAMPER_PEDAL /* Alias */
-#define CTL_PORTAMENTO 0x41
-#define CTL_SOSTENUTO 0x42
-#define CTL_SOFT_PEDAL 0x43
-/* undefined 0x44 */
-#define CTL_HOLD2 0x45
-/* undefined 0x46 - 0x4f */
-
-#define CTL_GENERAL_PURPOSE5 0x50
-#define CTL_GENERAL_PURPOSE6 0x51
-#define CTL_GENERAL_PURPOSE7 0x52
-#define CTL_GENERAL_PURPOSE8 0x53
-/* undefined 0x54 - 0x5a */
-#define CTL_EXT_EFF_DEPTH 0x5b
-#define CTL_TREMOLO_DEPTH 0x5c
-#define CTL_CHORUS_DEPTH 0x5d
-#define CTL_DETUNE_DEPTH 0x5e
-#define CTL_CELESTE_DEPTH CTL_DETUNE_DEPTH /* Alias for the above one */
-#define CTL_PHASER_DEPTH 0x5f
-#define CTL_DATA_INCREMENT 0x60
-#define CTL_DATA_DECREMENT 0x61
-#define CTL_NONREG_PARM_NUM_LSB 0x62
-#define CTL_NONREG_PARM_NUM_MSB 0x63
-#define CTL_REGIST_PARM_NUM_LSB 0x64
-#define CTL_REGIST_PARM_NUM_MSB 0x65
-/* undefined 0x66 - 0x78 */
-/* reserved 0x79 - 0x7f */
-
-/* Pseudo controllers (not midi compatible) */
-#define CTRL_PITCH_BENDER 255
-#define CTRL_PITCH_BENDER_RANGE 254
-#define CTRL_EXPRESSION 253 /* Obsolete */
-#define CTRL_MAIN_VOLUME 252 /* Obsolete */
-
-#define SEQ_BALANCE 11
-#define SEQ_VOLMODE 12
-
-/*
- * Volume mode decides how volumes are used
- */
-
-#define VOL_METHOD_ADAGIO 1
-#define VOL_METHOD_LINEAR 2
-
-/*
- * Note! SEQ_WAIT, SEQ_MIDIPUTC and SEQ_ECHO are used also as
- * input events.
- */
-
-/*
- * Event codes 0xf0 to 0xfc are reserved for future extensions.
- */
-
-#define SEQ_FULLSIZE 0xfd /* Long events */
-/*
- * SEQ_FULLSIZE events are used for loading patches/samples to the
- * synthesizer devices. These events are passed directly to the driver
- * of the associated synthesizer device. There is no limit to the size
- * of the extended events. These events are not queued but executed
- * immediately when the write() is called (execution can take several
- * seconds of time).
- *
- * When a SEQ_FULLSIZE message is written to the device, it must
- * be written using exactly one write() call. Other events cannot
- * be mixed to the same write.
- *
- * For FM synths (YM3812/OPL3) use struct sbi_instrument and write
- * it to the /dev/sequencer. Don't write other data together with
- * the instrument structure Set the key field of the structure to
- * FM_PATCH. The device field is used to route the patch to the
- * corresponding device.
- *
- * For Gravis UltraSound use struct patch_info. Initialize the key field
- * to GUS_PATCH.
- */
-#define SEQ_PRIVATE 0xfe /* Low level HW dependent events (8 bytes) */
-#define SEQ_EXTENDED 0xff /* Extended events (8 bytes) OBSOLETE */
-
-/*
- * Record for FM patches
- */
-
-typedef u_char sbi_instr_data[32];
-
-struct sbi_instrument {
- u_short key; /* FM_PATCH or OPL3_PATCH */
-#define FM_PATCH _PATCHKEY(0x01)
-#define OPL3_PATCH _PATCHKEY(0x03)
- short device; /* Synth# (0-4) */
- int channel; /* Program# to be initialized */
- sbi_instr_data operators; /* Reg. settings for operator cells
- * (.SBI format) */
-};
-
-struct synth_info { /* Read only */
- char name[30];
- int device; /* 0-N. INITIALIZE BEFORE CALLING */
- int synth_type;
-#define SYNTH_TYPE_FM 0
-#define SYNTH_TYPE_SAMPLE 1
-#define SYNTH_TYPE_MIDI 2 /* Midi interface */
-
- int synth_subtype;
-#define FM_TYPE_ADLIB 0x00
-#define FM_TYPE_OPL3 0x01
-#define MIDI_TYPE_MPU401 0x401
-
-#define SAMPLE_TYPE_BASIC 0x10
-#define SAMPLE_TYPE_GUS SAMPLE_TYPE_BASIC
-#define SAMPLE_TYPE_AWE32 0x20
-
- int perc_mode; /* No longer supported */
- int nr_voices;
- int nr_drums; /* Obsolete field */
- int instr_bank_size;
- u_long capabilities;
-#define SYNTH_CAP_PERCMODE 0x00000001 /* No longer used */
-#define SYNTH_CAP_OPL3 0x00000002 /* Set if OPL3 supported */
-#define SYNTH_CAP_INPUT 0x00000004 /* Input (MIDI) device */
- int dummies[19]; /* Reserve space */
-};
-
-struct sound_timer_info {
- char name[32];
- int caps;
-};
-
-struct midi_info {
- char name[30];
- int device; /* 0-N. INITIALIZE BEFORE CALLING */
- u_long capabilities; /* To be defined later */
- int dev_type;
- int dummies[18]; /* Reserve space */
-};
-
-/*
- * ioctl commands for the /dev/midi##
- */
-typedef struct {
- u_char cmd;
- char nr_args, nr_returns;
- u_char data[30];
-} mpu_command_rec;
-
-#define SNDCTL_MIDI_PRETIME _IOWR('m', 0, int)
-#define SNDCTL_MIDI_MPUMODE _IOWR('m', 1, int)
-#define SNDCTL_MIDI_MPUCMD _IOWR('m', 2, mpu_command_rec)
-#define MIOSPASSTHRU _IOWR('m', 3, int)
-#define MIOGPASSTHRU _IOWR('m', 4, int)
-
-/*
- * IOCTL commands for /dev/dsp and /dev/audio
- */
-
-#define SNDCTL_DSP_RESET _IO ('P', 0)
-#define SNDCTL_DSP_SYNC _IO ('P', 1)
-#define SNDCTL_DSP_SPEED _IOWR('P', 2, int)
-#define SNDCTL_DSP_STEREO _IOWR('P', 3, int)
-#define SNDCTL_DSP_GETBLKSIZE _IOR('P', 4, int)
-#define SNDCTL_DSP_SETBLKSIZE _IOW('P', 4, int)
-#define SNDCTL_DSP_SETFMT _IOWR('P',5, int) /* Selects ONE fmt*/
-
-/*
- * SOUND_PCM_WRITE_CHANNELS is not that different
- * from SNDCTL_DSP_STEREO
- */
-#define SOUND_PCM_WRITE_CHANNELS _IOWR('P', 6, int)
-#define SNDCTL_DSP_CHANNELS SOUND_PCM_WRITE_CHANNELS
-#define SOUND_PCM_WRITE_FILTER _IOWR('P', 7, int)
-#define SNDCTL_DSP_POST _IO ('P', 8)
-
-/*
- * SNDCTL_DSP_SETBLKSIZE and the following two calls mostly do
- * the same thing, i.e. set the block size used in DMA transfers.
- */
-#define SNDCTL_DSP_SUBDIVIDE _IOWR('P', 9, int)
-#define SNDCTL_DSP_SETFRAGMENT _IOWR('P',10, int)
-
-
-#define SNDCTL_DSP_GETFMTS _IOR ('P',11, int) /* Returns a mask */
-/*
- * Buffer status queries.
- */
-typedef struct audio_buf_info {
- int fragments; /* # of avail. frags (partly used ones not counted) */
- int fragstotal; /* Total # of fragments allocated */
- int fragsize; /* Size of a fragment in bytes */
-
- int bytes; /* Avail. space in bytes (includes partly used fragments) */
- /* Note! 'bytes' could be more than fragments*fragsize */
-} audio_buf_info;
-
-#define SNDCTL_DSP_GETOSPACE _IOR ('P',12, audio_buf_info)
-#define SNDCTL_DSP_GETISPACE _IOR ('P',13, audio_buf_info)
-
-/*
- * SNDCTL_DSP_NONBLOCK is the same (but less powerful, since the
- * action cannot be undone) of FIONBIO. The same can be achieved
- * by opening the device with O_NDELAY
- */
-#define SNDCTL_DSP_NONBLOCK _IO ('P',14)
-
-#define SNDCTL_DSP_GETCAPS _IOR ('P',15, int)
-#define DSP_CAP_REVISION 0x000000ff /* revision level (0 to 255) */
-#define DSP_CAP_DUPLEX 0x00000100 /* Full duplex record/playback */
-#define DSP_CAP_REALTIME 0x00000200 /* Real time capability */
-#define DSP_CAP_BATCH 0x00000400
- /*
- * Device has some kind of internal buffers which may
- * cause some delays and decrease precision of timing
- */
-#define DSP_CAP_COPROC 0x00000800
- /* Has a coprocessor, sometimes it's a DSP but usually not */
-#define DSP_CAP_TRIGGER 0x00001000 /* Supports SETTRIGGER */
-#define DSP_CAP_MMAP 0x00002000 /* Supports mmap() */
-
-/*
- * What do these function do ?
- */
-#define SNDCTL_DSP_GETTRIGGER _IOR ('P',16, int)
-#define SNDCTL_DSP_SETTRIGGER _IOW ('P',16, int)
-#define PCM_ENABLE_INPUT 0x00000001
-#define PCM_ENABLE_OUTPUT 0x00000002
-
-typedef struct count_info {
- int bytes; /* Total # of bytes processed */
- int blocks; /* # of fragment transitions since last time */
- int ptr; /* Current DMA pointer value */
-} count_info;
-
-/*
- * GETIPTR and GETISPACE are not that different... same for out.
- */
-#define SNDCTL_DSP_GETIPTR _IOR ('P',17, count_info)
-#define SNDCTL_DSP_GETOPTR _IOR ('P',18, count_info)
-
-typedef struct buffmem_desc {
- caddr_t buffer;
- int size;
-} buffmem_desc;
-
-#define SNDCTL_DSP_MAPINBUF _IOR ('P', 19, buffmem_desc)
-#define SNDCTL_DSP_MAPOUTBUF _IOR ('P', 20, buffmem_desc)
-#define SNDCTL_DSP_SETSYNCRO _IO ('P', 21)
-#define SNDCTL_DSP_SETDUPLEX _IO ('P', 22)
-#define SNDCTL_DSP_GETODELAY _IOR ('P', 23, int)
-
-/*
- * I guess these are the readonly version of the same
- * functions that exist above as SNDCTL_DSP_...
- */
-#define SOUND_PCM_READ_RATE _IOR ('P', 2, int)
-#define SOUND_PCM_READ_CHANNELS _IOR ('P', 6, int)
-#define SOUND_PCM_READ_BITS _IOR ('P', 5, int)
-#define SOUND_PCM_READ_FILTER _IOR ('P', 7, int)
-
-/*
- * ioctl calls to be used in communication with coprocessors and
- * DSP chips.
- */
-
-typedef struct copr_buffer {
- int command; /* Set to 0 if not used */
- int flags;
-#define CPF_NONE 0x0000
-#define CPF_FIRST 0x0001 /* First block */
-#define CPF_LAST 0x0002 /* Last block */
- int len;
- int offs; /* If required by the device (0 if not used) */
-
- u_char data[4000]; /* NOTE! 4000 is not 4k */
-} copr_buffer;
-
-typedef struct copr_debug_buf {
- int command; /* Used internally. Set to 0 */
- int parm1;
- int parm2;
- int flags;
- int len; /* Length of data in bytes */
-} copr_debug_buf;
-
-typedef struct copr_msg {
- int len;
- u_char data[4000];
-} copr_msg;
-
-#define SNDCTL_COPR_RESET _IO ('C', 0)
-#define SNDCTL_COPR_LOAD _IOWR('C', 1, copr_buffer)
-#define SNDCTL_COPR_RDATA _IOWR('C', 2, copr_debug_buf)
-#define SNDCTL_COPR_RCODE _IOWR('C', 3, copr_debug_buf)
-#define SNDCTL_COPR_WDATA _IOW ('C', 4, copr_debug_buf)
-#define SNDCTL_COPR_WCODE _IOW ('C', 5, copr_debug_buf)
-#define SNDCTL_COPR_RUN _IOWR('C', 6, copr_debug_buf)
-#define SNDCTL_COPR_HALT _IOWR('C', 7, copr_debug_buf)
-#define SNDCTL_COPR_SENDMSG _IOW ('C', 8, copr_msg)
-#define SNDCTL_COPR_RCVMSG _IOR ('C', 9, copr_msg)
-
-/*
- * IOCTL commands for /dev/mixer
- */
-
-/*
- * Mixer devices
- *
- * There can be up to 20 different analog mixer channels. The
- * SOUND_MIXER_NRDEVICES gives the currently supported maximum.
- * The SOUND_MIXER_READ_DEVMASK returns a bitmask which tells
- * the devices supported by the particular mixer.
- */
-
-#define SOUND_MIXER_NRDEVICES 25
-#define SOUND_MIXER_VOLUME 0 /* Master output level */
-#define SOUND_MIXER_BASS 1 /* Treble level of all output channels */
-#define SOUND_MIXER_TREBLE 2 /* Bass level of all output channels */
-#define SOUND_MIXER_SYNTH 3 /* Volume of synthesier input */
-#define SOUND_MIXER_PCM 4 /* Output level for the audio device */
-#define SOUND_MIXER_SPEAKER 5 /* Output level for the PC speaker
- * signals */
-#define SOUND_MIXER_LINE 6 /* Volume level for the line in jack */
-#define SOUND_MIXER_MIC 7 /* Volume for the signal coming from
- * the microphone jack */
-#define SOUND_MIXER_CD 8 /* Volume level for the input signal
- * connected to the CD audio input */
-#define SOUND_MIXER_IMIX 9 /* Recording monitor. It controls the
- * output volume of the selected
- * recording sources while recording */
-#define SOUND_MIXER_ALTPCM 10 /* Volume of the alternative codec
- * device */
-#define SOUND_MIXER_RECLEV 11 /* Global recording level */
-#define SOUND_MIXER_IGAIN 12 /* Input gain */
-#define SOUND_MIXER_OGAIN 13 /* Output gain */
-/*
- * The AD1848 codec and compatibles have three line level inputs
- * (line, aux1 and aux2). Since each card manufacturer have assigned
- * different meanings to these inputs, it's inpractical to assign
- * specific meanings (line, cd, synth etc.) to them.
- */
-#define SOUND_MIXER_LINE1 14 /* Input source 1 (aux1) */
-#define SOUND_MIXER_LINE2 15 /* Input source 2 (aux2) */
-#define SOUND_MIXER_LINE3 16 /* Input source 3 (line) */
-#define SOUND_MIXER_DIGITAL1 17 /* Digital (input) 1 */
-#define SOUND_MIXER_DIGITAL2 18 /* Digital (input) 2 */
-#define SOUND_MIXER_DIGITAL3 19 /* Digital (input) 3 */
-#define SOUND_MIXER_PHONEIN 20 /* Phone input */
-#define SOUND_MIXER_PHONEOUT 21 /* Phone output */
-#define SOUND_MIXER_VIDEO 22 /* Video/TV (audio) in */
-#define SOUND_MIXER_RADIO 23 /* Radio in */
-#define SOUND_MIXER_MONITOR 24 /* Monitor (usually mic) volume */
-
-
-/*
- * Some on/off settings (SOUND_SPECIAL_MIN - SOUND_SPECIAL_MAX)
- * Not counted to SOUND_MIXER_NRDEVICES, but use the same number space
- */
-#define SOUND_ONOFF_MIN 28
-#define SOUND_ONOFF_MAX 30
-#define SOUND_MIXER_MUTE 28 /* 0 or 1 */
-#define SOUND_MIXER_ENHANCE 29 /* Enhanced stereo (0, 40, 60 or 80) */
-#define SOUND_MIXER_LOUD 30 /* 0 or 1 */
-
-/* Note! Number 31 cannot be used since the sign bit is reserved */
-#define SOUND_MIXER_NONE 31
-
-#define SOUND_DEVICE_LABELS { \
- "Vol ", "Bass ", "Trebl", "Synth", "Pcm ", "Spkr ", "Line ", \
- "Mic ", "CD ", "Mix ", "Pcm2 ", "Rec ", "IGain", "OGain", \
- "Line1", "Line2", "Line3", "Digital1", "Digital2", "Digital3", \
- "PhoneIn", "PhoneOut", "Video", "Radio", "Monitor"}
-
-#define SOUND_DEVICE_NAMES { \
- "vol", "bass", "treble", "synth", "pcm", "speaker", "line", \
- "mic", "cd", "mix", "pcm2", "rec", "igain", "ogain", \
- "line1", "line2", "line3", "dig1", "dig2", "dig3", \
- "phin", "phout", "video", "radio", "monitor"}
-
-/* Device bitmask identifiers */
-
-#define SOUND_MIXER_RECSRC 0xff /* 1 bit per recording source */
-#define SOUND_MIXER_DEVMASK 0xfe /* 1 bit per supported device */
-#define SOUND_MIXER_RECMASK 0xfd /* 1 bit per supp. recording source */
-#define SOUND_MIXER_CAPS 0xfc
-#define SOUND_CAP_EXCL_INPUT 0x00000001 /* Only 1 rec. src at a time */
-#define SOUND_MIXER_STEREODEVS 0xfb /* Mixer channels supporting stereo */
-
-/* Device mask bits */
-
-#define SOUND_MASK_VOLUME (1 << SOUND_MIXER_VOLUME)
-#define SOUND_MASK_BASS (1 << SOUND_MIXER_BASS)
-#define SOUND_MASK_TREBLE (1 << SOUND_MIXER_TREBLE)
-#define SOUND_MASK_SYNTH (1 << SOUND_MIXER_SYNTH)
-#define SOUND_MASK_PCM (1 << SOUND_MIXER_PCM)
-#define SOUND_MASK_SPEAKER (1 << SOUND_MIXER_SPEAKER)
-#define SOUND_MASK_LINE (1 << SOUND_MIXER_LINE)
-#define SOUND_MASK_MIC (1 << SOUND_MIXER_MIC)
-#define SOUND_MASK_CD (1 << SOUND_MIXER_CD)
-#define SOUND_MASK_IMIX (1 << SOUND_MIXER_IMIX)
-#define SOUND_MASK_ALTPCM (1 << SOUND_MIXER_ALTPCM)
-#define SOUND_MASK_RECLEV (1 << SOUND_MIXER_RECLEV)
-#define SOUND_MASK_IGAIN (1 << SOUND_MIXER_IGAIN)
-#define SOUND_MASK_OGAIN (1 << SOUND_MIXER_OGAIN)
-#define SOUND_MASK_LINE1 (1 << SOUND_MIXER_LINE1)
-#define SOUND_MASK_LINE2 (1 << SOUND_MIXER_LINE2)
-#define SOUND_MASK_LINE3 (1 << SOUND_MIXER_LINE3)
-#define SOUND_MASK_DIGITAL1 (1 << SOUND_MIXER_DIGITAL1)
-#define SOUND_MASK_DIGITAL2 (1 << SOUND_MIXER_DIGITAL2)
-#define SOUND_MASK_DIGITAL3 (1 << SOUND_MIXER_DIGITAL3)
-#define SOUND_MASK_PHONEIN (1 << SOUND_MIXER_PHONEIN)
-#define SOUND_MASK_PHONEOUT (1 << SOUND_MIXER_PHONEOUT)
-#define SOUND_MASK_RADIO (1 << SOUND_MIXER_RADIO)
-#define SOUND_MASK_VIDEO (1 << SOUND_MIXER_VIDEO)
-#define SOUND_MASK_MONITOR (1 << SOUND_MIXER_MONITOR)
-
-/* Obsolete macros */
-#define SOUND_MASK_MUTE (1 << SOUND_MIXER_MUTE)
-#define SOUND_MASK_ENHANCE (1 << SOUND_MIXER_ENHANCE)
-#define SOUND_MASK_LOUD (1 << SOUND_MIXER_LOUD)
-
-#define MIXER_READ(dev) _IOR('M', dev, int)
-#define SOUND_MIXER_READ_VOLUME MIXER_READ(SOUND_MIXER_VOLUME)
-#define SOUND_MIXER_READ_BASS MIXER_READ(SOUND_MIXER_BASS)
-#define SOUND_MIXER_READ_TREBLE MIXER_READ(SOUND_MIXER_TREBLE)
-#define SOUND_MIXER_READ_SYNTH MIXER_READ(SOUND_MIXER_SYNTH)
-#define SOUND_MIXER_READ_PCM MIXER_READ(SOUND_MIXER_PCM)
-#define SOUND_MIXER_READ_SPEAKER MIXER_READ(SOUND_MIXER_SPEAKER)
-#define SOUND_MIXER_READ_LINE MIXER_READ(SOUND_MIXER_LINE)
-#define SOUND_MIXER_READ_MIC MIXER_READ(SOUND_MIXER_MIC)
-#define SOUND_MIXER_READ_CD MIXER_READ(SOUND_MIXER_CD)
-#define SOUND_MIXER_READ_IMIX MIXER_READ(SOUND_MIXER_IMIX)
-#define SOUND_MIXER_READ_ALTPCM MIXER_READ(SOUND_MIXER_ALTPCM)
-#define SOUND_MIXER_READ_RECLEV MIXER_READ(SOUND_MIXER_RECLEV)
-#define SOUND_MIXER_READ_IGAIN MIXER_READ(SOUND_MIXER_IGAIN)
-#define SOUND_MIXER_READ_OGAIN MIXER_READ(SOUND_MIXER_OGAIN)
-#define SOUND_MIXER_READ_LINE1 MIXER_READ(SOUND_MIXER_LINE1)
-#define SOUND_MIXER_READ_LINE2 MIXER_READ(SOUND_MIXER_LINE2)
-#define SOUND_MIXER_READ_LINE3 MIXER_READ(SOUND_MIXER_LINE3)
-#define SOUND_MIXER_READ_DIGITAL1 MIXER_READ(SOUND_MIXER_DIGITAL1)
-#define SOUND_MIXER_READ_DIGITAL2 MIXER_READ(SOUND_MIXER_DIGITAL2)
-#define SOUND_MIXER_READ_DIGITAL3 MIXER_READ(SOUND_MIXER_DIGITAL3)
-#define SOUND_MIXER_READ_PHONEIN MIXER_READ(SOUND_MIXER_PHONEIN)
-#define SOUND_MIXER_READ_PHONEOUT MIXER_READ(SOUND_MIXER_PHONEOUT)
-#define SOUND_MIXER_READ_RADIO MIXER_READ(SOUND_MIXER_RADIO)
-#define SOUND_MIXER_READ_VIDEO MIXER_READ(SOUND_MIXER_VIDEO)
-#define SOUND_MIXER_READ_MONITOR MIXER_READ(SOUND_MIXER_MONITOR)
-
-/* Obsolete macros */
-#define SOUND_MIXER_READ_MUTE MIXER_READ(SOUND_MIXER_MUTE)
-#define SOUND_MIXER_READ_ENHANCE MIXER_READ(SOUND_MIXER_ENHANCE)
-#define SOUND_MIXER_READ_LOUD MIXER_READ(SOUND_MIXER_LOUD)
-
-#define SOUND_MIXER_READ_RECSRC MIXER_READ(SOUND_MIXER_RECSRC)
-#define SOUND_MIXER_READ_DEVMASK MIXER_READ(SOUND_MIXER_DEVMASK)
-#define SOUND_MIXER_READ_RECMASK MIXER_READ(SOUND_MIXER_RECMASK)
-#define SOUND_MIXER_READ_STEREODEVS MIXER_READ(SOUND_MIXER_STEREODEVS)
-#define SOUND_MIXER_READ_CAPS MIXER_READ(SOUND_MIXER_CAPS)
-
-#define MIXER_WRITE(dev) _IOWR('M', dev, int)
-#define SOUND_MIXER_WRITE_VOLUME MIXER_WRITE(SOUND_MIXER_VOLUME)
-#define SOUND_MIXER_WRITE_BASS MIXER_WRITE(SOUND_MIXER_BASS)
-#define SOUND_MIXER_WRITE_TREBLE MIXER_WRITE(SOUND_MIXER_TREBLE)
-#define SOUND_MIXER_WRITE_SYNTH MIXER_WRITE(SOUND_MIXER_SYNTH)
-#define SOUND_MIXER_WRITE_PCM MIXER_WRITE(SOUND_MIXER_PCM)
-#define SOUND_MIXER_WRITE_SPEAKER MIXER_WRITE(SOUND_MIXER_SPEAKER)
-#define SOUND_MIXER_WRITE_LINE MIXER_WRITE(SOUND_MIXER_LINE)
-#define SOUND_MIXER_WRITE_MIC MIXER_WRITE(SOUND_MIXER_MIC)
-#define SOUND_MIXER_WRITE_CD MIXER_WRITE(SOUND_MIXER_CD)
-#define SOUND_MIXER_WRITE_IMIX MIXER_WRITE(SOUND_MIXER_IMIX)
-#define SOUND_MIXER_WRITE_ALTPCM MIXER_WRITE(SOUND_MIXER_ALTPCM)
-#define SOUND_MIXER_WRITE_RECLEV MIXER_WRITE(SOUND_MIXER_RECLEV)
-#define SOUND_MIXER_WRITE_IGAIN MIXER_WRITE(SOUND_MIXER_IGAIN)
-#define SOUND_MIXER_WRITE_OGAIN MIXER_WRITE(SOUND_MIXER_OGAIN)
-#define SOUND_MIXER_WRITE_LINE1 MIXER_WRITE(SOUND_MIXER_LINE1)
-#define SOUND_MIXER_WRITE_LINE2 MIXER_WRITE(SOUND_MIXER_LINE2)
-#define SOUND_MIXER_WRITE_LINE3 MIXER_WRITE(SOUND_MIXER_LINE3)
-#define SOUND_MIXER_WRITE_DIGITAL1 MIXER_WRITE(SOUND_MIXER_DIGITAL1)
-#define SOUND_MIXER_WRITE_DIGITAL2 MIXER_WRITE(SOUND_MIXER_DIGITAL2)
-#define SOUND_MIXER_WRITE_DIGITAL3 MIXER_WRITE(SOUND_MIXER_DIGITAL3)
-#define SOUND_MIXER_WRITE_PHONEIN MIXER_WRITE(SOUND_MIXER_PHONEIN)
-#define SOUND_MIXER_WRITE_PHONEOUT MIXER_WRITE(SOUND_MIXER_PHONEOUT)
-#define SOUND_MIXER_WRITE_RADIO MIXER_WRITE(SOUND_MIXER_RADIO)
-#define SOUND_MIXER_WRITE_VIDEO MIXER_WRITE(SOUND_MIXER_VIDEO)
-#define SOUND_MIXER_WRITE_MONITOR MIXER_WRITE(SOUND_MIXER_MONITOR)
-
-#define SOUND_MIXER_WRITE_MUTE MIXER_WRITE(SOUND_MIXER_MUTE)
-#define SOUND_MIXER_WRITE_ENHANCE MIXER_WRITE(SOUND_MIXER_ENHANCE)
-#define SOUND_MIXER_WRITE_LOUD MIXER_WRITE(SOUND_MIXER_LOUD)
-
-#define SOUND_MIXER_WRITE_RECSRC MIXER_WRITE(SOUND_MIXER_RECSRC)
-
-typedef struct mixer_info {
- char id[16];
- char name[32];
- int modify_counter;
- int fillers[10];
-} mixer_info;
-
-#define SOUND_MIXER_INFO _IOR('M', 101, mixer_info)
-
-#define LEFT_CHN 0
-#define RIGHT_CHN 1
-
-/*
- * Level 2 event types for /dev/sequencer
- */
-
-/*
- * The 4 most significant bits of byte 0 specify the class of
- * the event:
- *
- * 0x8X = system level events,
- * 0x9X = device/port specific events, event[1] = device/port,
- * The last 4 bits give the subtype:
- * 0x02 = Channel event (event[3] = chn).
- * 0x01 = note event (event[4] = note).
- * (0x01 is not used alone but always with bit 0x02).
- * event[2] = MIDI message code (0x80=note off etc.)
- *
- */
-
-#define EV_SEQ_LOCAL 0x80
-#define EV_TIMING 0x81
-#define EV_CHN_COMMON 0x92
-#define EV_CHN_VOICE 0x93
-#define EV_SYSEX 0x94
-/*
- * Event types 200 to 220 are reserved for application use.
- * These numbers will not be used by the driver.
- */
-
-/*
- * Events for event type EV_CHN_VOICE
- */
-
-#define MIDI_NOTEOFF 0x80
-#define MIDI_NOTEON 0x90
-#define MIDI_KEY_PRESSURE 0xA0
-
-/*
- * Events for event type EV_CHN_COMMON
- */
-
-#define MIDI_CTL_CHANGE 0xB0
-#define MIDI_PGM_CHANGE 0xC0
-#define MIDI_CHN_PRESSURE 0xD0
-#define MIDI_PITCH_BEND 0xE0
-
-#define MIDI_SYSTEM_PREFIX 0xF0
-
-/*
- * Timer event types
- */
-#define TMR_WAIT_REL 1 /* Time relative to the prev time */
-#define TMR_WAIT_ABS 2 /* Absolute time since TMR_START */
-#define TMR_STOP 3
-#define TMR_START 4
-#define TMR_CONTINUE 5
-#define TMR_TEMPO 6
-#define TMR_ECHO 8
-#define TMR_CLOCK 9 /* MIDI clock */
-#define TMR_SPP 10 /* Song position pointer */
-#define TMR_TIMESIG 11 /* Time signature */
-
-/*
- * Local event types
- */
-#define LOCL_STARTAUDIO 1
-
-#if (!defined(_KERNEL) && !defined(INKERNEL)) || defined(USE_SEQ_MACROS)
-/*
- * Some convenience macros to simplify programming of the
- * /dev/sequencer interface
- *
- * These macros define the API which should be used when possible.
- */
-
-#ifndef USE_SIMPLE_MACROS
-void seqbuf_dump(void); /* This function must be provided by programs */
-
-/* Sample seqbuf_dump() implementation:
- *
- * SEQ_DEFINEBUF (2048); -- Defines a buffer for 2048 bytes
- *
- * int seqfd; -- The file descriptor for /dev/sequencer.
- *
- * void
- * seqbuf_dump ()
- * {
- * if (_seqbufptr)
- * if (write (seqfd, _seqbuf, _seqbufptr) == -1)
- * {
- * perror ("write /dev/sequencer");
- * exit (-1);
- * }
- * _seqbufptr = 0;
- * }
- */
-
-#define SEQ_DEFINEBUF(len) \
- u_char _seqbuf[len]; int _seqbuflen = len;int _seqbufptr = 0
-#define SEQ_USE_EXTBUF() \
- extern u_char _seqbuf[]; \
- extern int _seqbuflen;extern int _seqbufptr
-#define SEQ_DECLAREBUF() SEQ_USE_EXTBUF()
-#define SEQ_PM_DEFINES struct patmgr_info _pm_info
-#define _SEQ_NEEDBUF(len) \
- if ((_seqbufptr+(len)) > _seqbuflen) \
- seqbuf_dump()
-#define _SEQ_ADVBUF(len) _seqbufptr += len
-#define SEQ_DUMPBUF seqbuf_dump
-#else
-/*
- * This variation of the sequencer macros is used just to format one event
- * using fixed buffer.
- *
- * The program using the macro library must define the following macros before
- * using this library.
- *
- * #define _seqbuf name of the buffer (u_char[])
- * #define _SEQ_ADVBUF(len) If the applic needs to know the exact
- * size of the event, this macro can be used.
- * Otherwise this must be defined as empty.
- * #define _seqbufptr Define the name of index variable or 0 if
- * not required.
- */
-#define _SEQ_NEEDBUF(len) /* empty */
-#endif
-
-#define PM_LOAD_PATCH(dev, bank, pgm) \
- (SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \
- _pm_info.device=dev, _pm_info.data.data8[0]=pgm, \
- _pm_info.parm1 = bank, _pm_info.parm2 = 1, \
- ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info))
-#define PM_LOAD_PATCHES(dev, bank, pgm) \
- (SEQ_DUMPBUF(), _pm_info.command = _PM_LOAD_PATCH, \
- _pm_info.device=dev, bcopy( pgm, _pm_info.data.data8, 128), \
- _pm_info.parm1 = bank, _pm_info.parm2 = 128, \
- ioctl(seqfd, SNDCTL_PMGR_ACCESS, &_pm_info))
-
-#define SEQ_VOLUME_MODE(dev, mode) { \
- _SEQ_NEEDBUF(8);\
- _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
- _seqbuf[_seqbufptr+1] = SEQ_VOLMODE;\
- _seqbuf[_seqbufptr+2] = (dev);\
- _seqbuf[_seqbufptr+3] = (mode);\
- _seqbuf[_seqbufptr+4] = 0;\
- _seqbuf[_seqbufptr+5] = 0;\
- _seqbuf[_seqbufptr+6] = 0;\
- _seqbuf[_seqbufptr+7] = 0;\
- _SEQ_ADVBUF(8);}
-
-/*
- * Midi voice messages
- */
-
-#define _CHN_VOICE(dev, event, chn, note, parm) { \
- _SEQ_NEEDBUF(8);\
- _seqbuf[_seqbufptr] = EV_CHN_VOICE;\
- _seqbuf[_seqbufptr+1] = (dev);\
- _seqbuf[_seqbufptr+2] = (event);\
- _seqbuf[_seqbufptr+3] = (chn);\
- _seqbuf[_seqbufptr+4] = (note);\
- _seqbuf[_seqbufptr+5] = (parm);\
- _seqbuf[_seqbufptr+6] = (0);\
- _seqbuf[_seqbufptr+7] = 0;\
- _SEQ_ADVBUF(8);}
-
-#define SEQ_START_NOTE(dev, chn, note, vol) \
- _CHN_VOICE(dev, MIDI_NOTEON, chn, note, vol)
-
-#define SEQ_STOP_NOTE(dev, chn, note, vol) \
- _CHN_VOICE(dev, MIDI_NOTEOFF, chn, note, vol)
-
-#define SEQ_KEY_PRESSURE(dev, chn, note, pressure) \
- _CHN_VOICE(dev, MIDI_KEY_PRESSURE, chn, note, pressure)
-
-/*
- * Midi channel messages
- */
-
-#define _CHN_COMMON(dev, event, chn, p1, p2, w14) { \
- _SEQ_NEEDBUF(8);\
- _seqbuf[_seqbufptr] = EV_CHN_COMMON;\
- _seqbuf[_seqbufptr+1] = (dev);\
- _seqbuf[_seqbufptr+2] = (event);\
- _seqbuf[_seqbufptr+3] = (chn);\
- _seqbuf[_seqbufptr+4] = (p1);\
- _seqbuf[_seqbufptr+5] = (p2);\
- *(short *)&_seqbuf[_seqbufptr+6] = (w14);\
- _SEQ_ADVBUF(8);}
-/*
- * SEQ_SYSEX permits sending of sysex messages. (It may look that it permits
- * sending any MIDI bytes but it's absolutely not possible. Trying to do
- * so _will_ cause problems with MPU401 intelligent mode).
- *
- * Sysex messages are sent in blocks of 1 to 6 bytes. Longer messages must be
- * sent by calling SEQ_SYSEX() several times (there must be no other events
- * between them). First sysex fragment must have 0xf0 in the first byte
- * and the last byte (buf[len-1] of the last fragment must be 0xf7. No byte
- * between these sysex start and end markers cannot be larger than 0x7f. Also
- * lengths of each fragments (except the last one) must be 6.
- *
- * Breaking the above rules may work with some MIDI ports but is likely to
- * cause fatal problems with some other devices (such as MPU401).
- */
-#define SEQ_SYSEX(dev, buf, len) { \
- int i, l=(len); if (l>6)l=6;\
- _SEQ_NEEDBUF(8);\
- _seqbuf[_seqbufptr] = EV_SYSEX;\
- for(i=0;i<l;i++)_seqbuf[_seqbufptr+i+1] = (buf)[i];\
- for(i=l;i<6;i++)_seqbuf[_seqbufptr+i+1] = 0xff;\
- _SEQ_ADVBUF(8);}
-
-#define SEQ_CHN_PRESSURE(dev, chn, pressure) \
- _CHN_COMMON(dev, MIDI_CHN_PRESSURE, chn, pressure, 0, 0)
-
-#define SEQ_SET_PATCH(dev, chn, patch) \
- _CHN_COMMON(dev, MIDI_PGM_CHANGE, chn, patch, 0, 0)
-
-#define SEQ_CONTROL(dev, chn, controller, value) \
- _CHN_COMMON(dev, MIDI_CTL_CHANGE, chn, controller, 0, value)
-
-#define SEQ_BENDER(dev, chn, value) \
- _CHN_COMMON(dev, MIDI_PITCH_BEND, chn, 0, 0, value)
-
-
-#define SEQ_V2_X_CONTROL(dev, voice, controller, value) { \
- _SEQ_NEEDBUF(8);\
- _seqbuf[_seqbufptr] = SEQ_EXTENDED;\
- _seqbuf[_seqbufptr+1] = SEQ_CONTROLLER;\
- _seqbuf[_seqbufptr+2] = (dev);\
- _seqbuf[_seqbufptr+3] = (voice);\
- _seqbuf[_seqbufptr+4] = (controller);\
- *(short *)&_seqbuf[_seqbufptr+5] = (value);\
- _seqbuf[_seqbufptr+7] = 0;\
- _SEQ_ADVBUF(8);}
-
-/*
- * The following 5 macros are incorrectly implemented and obsolete.
- * Use SEQ_BENDER and SEQ_CONTROL (with proper controller) instead.
- */
-
-#define SEQ_PITCHBEND(dev, voice, value) \
- SEQ_V2_X_CONTROL(dev, voice, CTRL_PITCH_BENDER, value)
-#define SEQ_BENDER_RANGE(dev, voice, value) \
- SEQ_V2_X_CONTROL(dev, voice, CTRL_PITCH_BENDER_RANGE, value)
-#define SEQ_EXPRESSION(dev, voice, value) \
- SEQ_CONTROL(dev, voice, CTL_EXPRESSION, value*128)
-#define SEQ_MAIN_VOLUME(dev, voice, value) \
- SEQ_CONTROL(dev, voice, CTL_MAIN_VOLUME, (value*16383)/100)
-#define SEQ_PANNING(dev, voice, pos) \
- SEQ_CONTROL(dev, voice, CTL_PAN, (pos+128) / 2)
-
-/*
- * Timing and syncronization macros
- */
-
-#define _TIMER_EVENT(ev, parm) { \
- _SEQ_NEEDBUF(8);\
- _seqbuf[_seqbufptr+0] = EV_TIMING; \
- _seqbuf[_seqbufptr+1] = (ev); \
- _seqbuf[_seqbufptr+2] = 0;\
- _seqbuf[_seqbufptr+3] = 0;\
- *(u_int *)&_seqbuf[_seqbufptr+4] = (parm); \
- _SEQ_ADVBUF(8); \
- }
-
-#define SEQ_START_TIMER() _TIMER_EVENT(TMR_START, 0)
-#define SEQ_STOP_TIMER() _TIMER_EVENT(TMR_STOP, 0)
-#define SEQ_CONTINUE_TIMER() _TIMER_EVENT(TMR_CONTINUE, 0)
-#define SEQ_WAIT_TIME(ticks) _TIMER_EVENT(TMR_WAIT_ABS, ticks)
-#define SEQ_DELTA_TIME(ticks) _TIMER_EVENT(TMR_WAIT_REL, ticks)
-#define SEQ_ECHO_BACK(key) _TIMER_EVENT(TMR_ECHO, key)
-#define SEQ_SET_TEMPO(value) _TIMER_EVENT(TMR_TEMPO, value)
-#define SEQ_SONGPOS(pos) _TIMER_EVENT(TMR_SPP, pos)
-#define SEQ_TIME_SIGNATURE(sig) _TIMER_EVENT(TMR_TIMESIG, sig)
-
-/*
- * Local control events
- */
-
-#define _LOCAL_EVENT(ev, parm) { \
- _SEQ_NEEDBUF(8);\
- _seqbuf[_seqbufptr+0] = EV_SEQ_LOCAL; \
- _seqbuf[_seqbufptr+1] = (ev); \
- _seqbuf[_seqbufptr+2] = 0;\
- _seqbuf[_seqbufptr+3] = 0;\
- *(u_int *)&_seqbuf[_seqbufptr+4] = (parm); \
- _SEQ_ADVBUF(8); \
- }
-
-#define SEQ_PLAYAUDIO(devmask) _LOCAL_EVENT(LOCL_STARTAUDIO, devmask)
-/*
- * Events for the level 1 interface only
- */
-
-#define SEQ_MIDIOUT(device, byte) { \
- _SEQ_NEEDBUF(4);\
- _seqbuf[_seqbufptr] = SEQ_MIDIPUTC;\
- _seqbuf[_seqbufptr+1] = (byte);\
- _seqbuf[_seqbufptr+2] = (device);\
- _seqbuf[_seqbufptr+3] = 0;\
- _SEQ_ADVBUF(4);}
-
-/*
- * Patch loading.
- */
-#define SEQ_WRPATCH(patchx, len) { \
- if (_seqbufptr) seqbuf_dump(); \
- if (write(seqfd, (char*)(patchx), len)==-1) \
- perror("Write patch: /dev/sequencer"); \
- }
-
-#define SEQ_WRPATCH2(patchx, len) \
- ( seqbuf_dump(), write(seqfd, (char*)(patchx), len) )
-
-#endif
-
-/*
- * Here I have moved all the aliases for ioctl names.
- */
-
-#define SNDCTL_DSP_SAMPLESIZE SNDCTL_DSP_SETFMT
-#define SOUND_PCM_WRITE_BITS SNDCTL_DSP_SETFMT
-#define SOUND_PCM_SETFMT SNDCTL_DSP_SETFMT
-
-#define SOUND_PCM_WRITE_RATE SNDCTL_DSP_SPEED
-#define SOUND_PCM_POST SNDCTL_DSP_POST
-#define SOUND_PCM_RESET SNDCTL_DSP_RESET
-#define SOUND_PCM_SYNC SNDCTL_DSP_SYNC
-#define SOUND_PCM_SUBDIVIDE SNDCTL_DSP_SUBDIVIDE
-#define SOUND_PCM_SETFRAGMENT SNDCTL_DSP_SETFRAGMENT
-#define SOUND_PCM_GETFMTS SNDCTL_DSP_GETFMTS
-#define SOUND_PCM_GETOSPACE SNDCTL_DSP_GETOSPACE
-#define SOUND_PCM_GETISPACE SNDCTL_DSP_GETISPACE
-#define SOUND_PCM_NONBLOCK SNDCTL_DSP_NONBLOCK
-#define SOUND_PCM_GETCAPS SNDCTL_DSP_GETCAPS
-#define SOUND_PCM_GETTRIGGER SNDCTL_DSP_GETTRIGGER
-#define SOUND_PCM_SETTRIGGER SNDCTL_DSP_SETTRIGGER
-#define SOUND_PCM_SETSYNCRO SNDCTL_DSP_SETSYNCRO
-#define SOUND_PCM_GETIPTR SNDCTL_DSP_GETIPTR
-#define SOUND_PCM_GETOPTR SNDCTL_DSP_GETOPTR
-#define SOUND_PCM_MAPINBUF SNDCTL_DSP_MAPINBUF
-#define SOUND_PCM_MAPOUTBUF SNDCTL_DSP_MAPOUTBUF
-
-/***********************************************************************/
-
-/**
- * XXX OSSv4 defines -- some bits taken straight out of the new
- * sys/soundcard.h bundled with recent OSS releases.
- *
- * NB: These macros and structures will be reorganized and inserted
- * in appropriate places throughout this file once the code begins
- * to take shape.
- *
- * @todo reorganize layout more like the 4Front version
- * @todo ask about maintaining __SIOWR vs. _IOWR ioctl cmd defines
- */
-
-/**
- * @note The @c OSSV4_EXPERIMENT macro is meant to wrap new development code
- * in the sound system relevant to adopting 4Front's OSSv4 specification.
- * Users should not enable this! Really!
- */
-#if 0
-# define OSSV4_EXPERIMENT 1
-#else
-# undef OSSV4_EXPERIMENT
-#endif
-
-#ifdef SOUND_VERSION
-# undef SOUND_VERSION
-# define SOUND_VERSION 0x040000
-#endif /* !SOUND_VERSION */
-
-#define OSS_LONGNAME_SIZE 64
-#define OSS_LABEL_SIZE 16
-#define OSS_DEVNODE_SIZE 32
-typedef char oss_longname_t[OSS_LONGNAME_SIZE];
-typedef char oss_label_t[OSS_LABEL_SIZE];
-typedef char oss_devnode_t[OSS_DEVNODE_SIZE];
-
-typedef struct audio_errinfo
-{
- int play_underruns;
- int rec_overruns;
- unsigned int play_ptradjust;
- unsigned int rec_ptradjust;
- int play_errorcount;
- int rec_errorcount;
- int play_lasterror;
- int rec_lasterror;
- long play_errorparm;
- long rec_errorparm;
- int filler[16];
-} audio_errinfo;
-
-#define SNDCTL_DSP_GETPLAYVOL _IOR ('P', 24, int)
-#define SNDCTL_DSP_SETPLAYVOL _IOWR('P', 24, int)
-#define SNDCTL_DSP_GETERROR _IOR ('P', 25, audio_errinfo)
-
-
-/*
- ****************************************************************************
- * Sync groups for audio devices
- */
-typedef struct oss_syncgroup
-{
- int id;
- int mode;
- int filler[16];
-} oss_syncgroup;
-
-#define SNDCTL_DSP_SYNCGROUP _IOWR('P', 28, oss_syncgroup)
-#define SNDCTL_DSP_SYNCSTART _IOW ('P', 29, int)
-
-/*
- **************************************************************************
- * "cooked" mode enables software based conversions for sample rate, sample
- * format (bits) and number of channels (mono/stereo). These conversions are
- * required with some devices that support only one sample rate or just stereo
- * to let the applications to use other formats. The cooked mode is enabled by
- * default. However it's necessary to disable this mode when mmap() is used or
- * when very deterministic timing is required. SNDCTL_DSP_COOKEDMODE is an
- * optional call introduced in OSS 3.9.6f. It's _error return must be ignored_
- * since normally this call will return erno=EINVAL.
- *
- * SNDCTL_DSP_COOKEDMODE must be called immediately after open before doing
- * anything else. Otherwise the call will not have any effect.
- */
-#define SNDCTL_DSP_COOKEDMODE _IOW ('P', 30, int)
-
-/*
- **************************************************************************
- * SNDCTL_DSP_SILENCE and SNDCTL_DSP_SKIP are new calls in OSS 3.99.0
- * that can be used to implement pause/continue during playback (no effect
- * on recording).
- */
-#define SNDCTL_DSP_SILENCE _IO ('P', 31)
-#define SNDCTL_DSP_SKIP _IO ('P', 32)
-
-/*
- ****************************************************************************
- * Abort transfer (reset) functions for input and output
- */
-#define SNDCTL_DSP_HALT_INPUT _IO ('P', 33)
-#define SNDCTL_DSP_RESET_INPUT SNDCTL_DSP_HALT_INPUT /* Old name */
-#define SNDCTL_DSP_HALT_OUTPUT _IO ('P', 34)
-#define SNDCTL_DSP_RESET_OUTPUT SNDCTL_DSP_HALT_OUTPUT /* Old name */
-
-/*
- ****************************************************************************
- * Low water level control
- */
-#define SNDCTL_DSP_LOW_WATER _IOW ('P', 34, int)
-
-/** @todo Get rid of OSS_NO_LONG_LONG references? */
-
-/*
- ****************************************************************************
- * 64 bit pointer support. Only available in environments that support
- * the 64 bit (long long) integer type.
- */
-#ifndef OSS_NO_LONG_LONG
-typedef struct
-{
- long long samples;
- int fifo_samples;
- int filler[32]; /* For future use */
-} oss_count_t;
-
-#define SNDCTL_DSP_CURRENT_IPTR _IOR ('P', 35, oss_count_t)
-#define SNDCTL_DSP_CURRENT_OPTR _IOR ('P', 36, oss_count_t)
-#endif
-
-/*
- ****************************************************************************
- * Interface for selecting recording sources and playback output routings.
- */
-#define SNDCTL_DSP_GET_RECSRC_NAMES _IOR ('P', 37, oss_mixer_enuminfo)
-#define SNDCTL_DSP_GET_RECSRC _IOR ('P', 38, int)
-#define SNDCTL_DSP_SET_RECSRC _IOWR('P', 38, int)
-
-#define SNDCTL_DSP_GET_PLAYTGT_NAMES _IOR ('P', 39, oss_mixer_enuminfo)
-#define SNDCTL_DSP_GET_PLAYTGT _IOR ('P', 40, int)
-#define SNDCTL_DSP_SET_PLAYTGT _IOWR('P', 40, int)
-#define SNDCTL_DSP_GETRECVOL _IOR ('P', 41, int)
-#define SNDCTL_DSP_SETRECVOL _IOWR('P', 41, int)
-
-/*
- ***************************************************************************
- * Some calls for setting the channel assignment with multi channel devices
- * (see the manual for details). */
-#define SNDCTL_DSP_GET_CHNORDER _IOR ('P', 42, unsigned long long)
-#define SNDCTL_DSP_SET_CHNORDER _IOWR('P', 42, unsigned long long)
-# define CHID_UNDEF 0
-# define CHID_L 1 # define CHID_R 2
-# define CHID_C 3
-# define CHID_LFE 4
-# define CHID_LS 5
-# define CHID_RS 6
-# define CHID_LR 7
-# define CHID_RR 8
-#define CHNORDER_UNDEF 0x0000000000000000ULL
-#define CHNORDER_NORMAL 0x0000000087654321ULL
-
-#define MAX_PEAK_CHANNELS 128
-typedef unsigned short oss_peaks_t[MAX_PEAK_CHANNELS];
-#define SNDCTL_DSP_GETIPEAKS _IOR('P', 43, oss_peaks_t)
-#define SNDCTL_DSP_GETOPEAKS _IOR('P', 44, oss_peaks_t)
-#define SNDCTL_DSP_POLICY _IOW('P', 45, int) /* See the manual */
-
-/*
- * OSS_SYSIFO is obsolete. Use SNDCTL_SYSINFO insteads.
- */
-#define OSS_GETVERSION _IOR ('M', 118, int)
-
-/**
- * @brief Argument for SNDCTL_SYSINFO ioctl.
- *
- * For use w/ the SNDCTL_SYSINFO ioctl available on audio (/dev/dsp*),
- * mixer, and MIDI devices.
- */
-typedef struct oss_sysinfo
-{
- char product[32]; /* For example OSS/Free, OSS/Linux or
- OSS/Solaris */
- char version[32]; /* For example 4.0a */
- int versionnum; /* See OSS_GETVERSION */
- char options[128]; /* Reserved */
-
- int numaudios; /* # of audio/dsp devices */
- int openedaudio[8]; /* Bit mask telling which audio devices
- are busy */
-
- int numsynths; /* # of availavle synth devices */
- int nummidis; /* # of available MIDI ports */
- int numtimers; /* # of available timer devices */
- int nummixers; /* # of mixer devices */
-
- int openedmidi[8]; /* Bit mask telling which midi devices
- are busy */
- int numcards; /* Number of sound cards in the system */
- int filler[241]; /* For future expansion (set to -1) */
-} oss_sysinfo;
-
-typedef struct oss_mixext
-{
- int dev; /* Mixer device number */
- int ctrl; /* Controller number */
- int type; /* Entry type */
-# define MIXT_DEVROOT 0 /* Device root entry */
-# define MIXT_GROUP 1 /* Controller group */
-# define MIXT_ONOFF 2 /* OFF (0) or ON (1) */
-# define MIXT_ENUM 3 /* Enumerated (0 to maxvalue) */
-# define MIXT_MONOSLIDER 4 /* Mono slider (0 to 100) */
-# define MIXT_STEREOSLIDER 5 /* Stereo slider (dual 0 to 100) */
-# define MIXT_MESSAGE 6 /* (Readable) textual message */
-# define MIXT_MONOVU 7 /* VU meter value (mono) */
-# define MIXT_STEREOVU 8 /* VU meter value (stereo) */
-# define MIXT_MONOPEAK 9 /* VU meter peak value (mono) */
-# define MIXT_STEREOPEAK 10 /* VU meter peak value (stereo) */
-# define MIXT_RADIOGROUP 11 /* Radio button group */
-# define MIXT_MARKER 12 /* Separator between normal and extension entries */
-# define MIXT_VALUE 13 /* Decimal value entry */
-# define MIXT_HEXVALUE 14 /* Hexadecimal value entry */
-# define MIXT_MONODB 15 /* Mono atten. slider (0 to -144) */
-# define MIXT_STEREODB 16 /* Stereo atten. slider (dual 0 to -144) */
-# define MIXT_SLIDER 17 /* Slider (mono) with full integer range */
-# define MIXT_3D 18
-
- /* Possible value range (minvalue to maxvalue) */
- /* Note that maxvalue may also be smaller than minvalue */
- int maxvalue;
- int minvalue;
-
- int flags;
-# define MIXF_READABLE 0x00000001 /* Has readable value */
-# define MIXF_WRITEABLE 0x00000002 /* Has writeable value */
-# define MIXF_POLL 0x00000004 /* May change itself */
-# define MIXF_HZ 0x00000008 /* Herz scale */
-# define MIXF_STRING 0x00000010 /* Use dynamic extensions for value */
-# define MIXF_DYNAMIC 0x00000010 /* Supports dynamic extensions */
-# define MIXF_OKFAIL 0x00000020 /* Interpret value as 1=OK, 0=FAIL */
-# define MIXF_FLAT 0x00000040 /* Flat vertical space requirements */
-# define MIXF_LEGACY 0x00000080 /* Legacy mixer control group */
- char id[16]; /* Mnemonic ID (mainly for internal use) */
- int parent; /* Entry# of parent (group) node (-1 if root) */
-
- int dummy; /* Internal use */
-
- int timestamp;
-
- char data[64]; /* Misc data (entry type dependent) */
- unsigned char enum_present[32]; /* Mask of allowed enum values */
- int control_no; /* SOUND_MIXER_VOLUME..SOUND_MIXER_MIDI */
- /* (-1 means not indicated) */
-
-/*
- * The desc field is reserved for internal purposes of OSS. It should not be
- * used by applications.
- */
- unsigned int desc;
-#define MIXEXT_SCOPE_MASK 0x0000003f
-#define MIXEXT_SCOPE_OTHER 0x00000000
-#define MIXEXT_SCOPE_INPUT 0x00000001
-#define MIXEXT_SCOPE_OUTPUT 0x00000002
-#define MIXEXT_SCOPE_MONITOR 0x00000003
-#define MIXEXT_SCOPE_RECSWITCH 0x00000004
-
- char extname[32];
- int update_counter;
- int filler[7];
-} oss_mixext;
-
-typedef struct oss_mixext_root
-{
- char id[16];
- char name[48];
-} oss_mixext_root;
-
-typedef struct oss_mixer_value
-{
- int dev;
- int ctrl;
- int value;
- int flags; /* Reserved for future use. Initialize to 0 */
- int timestamp; /* Must be set to oss_mixext.timestamp */
- int filler[8]; /* Reserved for future use. Initialize to 0 */
-} oss_mixer_value;
-
-#define OSS_ENUM_MAXVALUE 255
-typedef struct oss_mixer_enuminfo
-{
- int dev;
- int ctrl;
- int nvalues;
- int version; /* Read the manual */
- short strindex[OSS_ENUM_MAXVALUE];
- char strings[3000];
-} oss_mixer_enuminfo;
-
-#define OPEN_READ PCM_ENABLE_INPUT
-#define OPEN_WRITE PCM_ENABLE_OUTPUT
-#define OPEN_READWRITE (OPEN_READ|OPEN_WRITE)
-
-/**
- * @brief Argument for SNDCTL_AUDIOINFO ioctl.
- *
- * For use w/ the SNDCTL_AUDIOINFO ioctl available on audio (/dev/dsp*)
- * devices.
- */
-typedef struct oss_audioinfo
-{
- int dev; /* Audio device number */
- char name[64];
- int busy; /* 0, OPEN_READ, OPEN_WRITE or OPEN_READWRITE */
- int pid;
- int caps; /* DSP_CAP_INPUT, DSP_CAP_OUTPUT */
- int iformats;
- int oformats;
- int magic; /* Reserved for internal use */
- char cmd[64]; /* Command using the device (if known) */
- int card_number;
- int port_number;
- int mixer_dev;
- int real_device; /* Obsolete field. Replaced by devnode */
- int enabled; /* 1=enabled, 0=device not ready at this
- moment */
- int flags; /* For internal use only - no practical
- meaning */
- int min_rate; /* Sample rate limits */
- int max_rate;
- int min_channels; /* Number of channels supported */
- int max_channels;
- int binding; /* DSP_BIND_FRONT, etc. 0 means undefined */
- int rate_source;
- char handle[32];
- #define OSS_MAX_SAMPLE_RATES 20 /* Cannot be changed */
- unsigned int nrates;
- unsigned int rates[OSS_MAX_SAMPLE_RATES]; /* Please read the manual before using these */
- oss_longname_t song_name; /* Song name (if given) */
- oss_label_t label; /* Device label (if given) */
- int latency; /* In usecs, -1=unknown */
- oss_devnode_t devnode; /* Device special file name (inside
- /dev) */
- int filler[186];
-} oss_audioinfo;
-
-typedef struct oss_mixerinfo
-{
- int dev;
- char id[16];
- char name[32];
- int modify_counter;
- int card_number;
- int port_number;
- char handle[32];
- int magic; /* Reserved */
- int enabled; /* Reserved */
- int caps;
-#define MIXER_CAP_VIRTUAL 0x00000001
- int flags; /* Reserved */
- int nrext;
- /*
- * The priority field can be used to select the default (motherboard)
- * mixer device. The mixer with the highest priority is the
- * most preferred one. -2 or less means that this device cannot be used
- * as the default mixer.
- */
- int priority;
- int filler[254]; /* Reserved */
-} oss_mixerinfo;
-
-typedef struct oss_midi_info
-{
- int dev; /* Midi device number */
- char name[64];
- int busy; /* 0, OPEN_READ, OPEN_WRITE or OPEN_READWRITE */
- int pid;
- char cmd[64]; /* Command using the device (if known) */
- int caps;
-#define MIDI_CAP_MPU401 0x00000001 /**** OBSOLETE ****/
-#define MIDI_CAP_INPUT 0x00000002
-#define MIDI_CAP_OUTPUT 0x00000004
-#define MIDI_CAP_INOUT (MIDI_CAP_INPUT|MIDI_CAP_OUTPUT)
-#define MIDI_CAP_VIRTUAL 0x00000008 /* Pseudo device */
-#define MIDI_CAP_MTCINPUT 0x00000010 /* Supports SNDCTL_MIDI_MTCINPUT */
-#define MIDI_CAP_CLIENT 0x00000020 /* Virtual client side device */
-#define MIDI_CAP_SERVER 0x00000040 /* Virtual server side device */
-#define MIDI_CAP_INTERNAL 0x00000080 /* Internal (synth) device */
-#define MIDI_CAP_EXTERNAL 0x00000100 /* external (MIDI port) device */
-#define MIDI_CAP_PTOP 0x00000200 /* Point to point link to one device */
-#define MIDI_CAP_MTC 0x00000400 /* MTC/SMPTE (control) device */
- int magic; /* Reserved for internal use */
- int card_number;
- int port_number;
- int enabled; /* 1=enabled, 0=device not ready at this moment */
- int flags; /* For internal use only - no practical meaning */
- char handle[32];
- oss_longname_t song_name; /* Song name (if known) */
- oss_label_t label; /* Device label (if given) */
- int latency; /* In usecs, -1=unknown */
- int filler[244];
-} oss_midi_info;
-
-typedef struct oss_card_info
-{
- int card;
- char shortname[16];
- char longname[128];
- int flags;
- int filler[256];
-} oss_card_info;
-
-#define SNDCTL_SYSINFO _IOR ('X', 1, oss_sysinfo)
-#define OSS_SYSINFO SNDCTL_SYSINFO /* Old name */
-
-#define SNDCTL_MIX_NRMIX _IOR ('X', 2, int)
-#define SNDCTL_MIX_NREXT _IOWR('X', 3, int)
-#define SNDCTL_MIX_EXTINFO _IOWR('X', 4, oss_mixext)
-#define SNDCTL_MIX_READ _IOWR('X', 5, oss_mixer_value)
-#define SNDCTL_MIX_WRITE _IOWR('X', 6, oss_mixer_value)
-
-#define SNDCTL_AUDIOINFO _IOWR('X', 7, oss_audioinfo)
-#define SNDCTL_MIX_ENUMINFO _IOWR('X', 8, oss_mixer_enuminfo)
-#define SNDCTL_MIDIINFO _IOWR('X', 9, oss_midi_info)
-#define SNDCTL_MIXERINFO _IOWR('X',10, oss_mixerinfo)
-#define SNDCTL_CARDINFO _IOWR('X',11, oss_card_info)
-
-/*
- * Few more "globally" available ioctl calls.
- */
-#define SNDCTL_SETSONG _IOW ('Y', 2, oss_longname_t)
-#define SNDCTL_GETSONG _IOR ('Y', 2, oss_longname_t)
-#define SNDCTL_SETNAME _IOW ('Y', 3, oss_longname_t)
-#define SNDCTL_SETLABEL _IOW ('Y', 4, oss_label_t)
-#define SNDCTL_GETLABEL _IOR ('Y', 4, oss_label_t)
-
-#endif /* !_SYS_SOUNDCARD_H_ */
-#define IDI_ICON1 101
\ No newline at end of file
+#define IDI_ICON1 101
#include <cassert>
#include "core/model/model.h"
-#include "core/channels/sampleChannel.h"
-#include "core/channels/midiChannel.h"
#include "core/clock.h"
#include "core/const.h"
#include "core/recorderHandler.h"
#include "core/recorder.h"
#include "core/action.h"
-#include "recorder.h"
+#include "glue/events.h"
+#include "glue/recorder.h"
#include "actionEditor.h"
{
namespace mr = m::recorder;
+ // TODO - use MidiEvent(float)
m::MidiEvent e1 = m::MidiEvent(m::MidiEvent::ENVELOPE, 0, G_MAX_VELOCITY);
m::MidiEvent e2 = m::MidiEvent(m::MidiEvent::ENVELOPE, 0, value);
const m::Action a1 = mr::rec(channelId, 0, e1);
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 = mr::rec(channelId, frame, e2);
bool isSinglePressMode_(ID channelId)
{
bool b;
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+ m::model::onGet(m::model::channels, channelId, [&](const m::Channel& c)
{
- const m::SampleChannel& sc = static_cast<m::SampleChannel&>(c);
- b = sc.mode == ChannelMode::SINGLE_PRESS;
+ b = c.samplePlayer->state->mode == SamplePlayerMode::SINGLE_PRESS;
});
return b;
}
-}; // {anonymous}
+} // {anonymous}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
+SampleData::SampleData(const m::SamplePlayer& s)
+: channelMode(s.state->mode.load())
+, isLoopMode (s.state->isAnyLoopMode())
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Data::Data(const m::Channel& c)
+: channelId (c.id)
+, channelName(c.state->name)
+, actions (m::recorder::getActionsOnChannel(c.id))
+{
+ if (c.getType() == ChannelType::SAMPLE)
+ sample = std::make_optional<SampleData>(*c.samplePlayer);
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+Data getData(ID channelId)
+{
+ namespace mm = m::model;
+
+ mm::ChannelsLock cl(mm::channels);
+ return Data(mm::get(mm::channels, channelId));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
void recordMidiAction(ID channelId, int note, int velocity, Frame f1, Frame f2)
{
namespace mr = m::recorder;
void deleteMidiAction(ID channelId, const m::Action& a)
{
namespace mr = m::recorder;
- namespace cr = c::recorder;
assert(a.isValid());
assert(a.event.getStatus() == m::MidiEvent::NOTE_ON);
key_on/key_off sequence. Check if 'next' exist first: could be orphaned. */
if (a.next != nullptr) {
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- m::MidiChannel& mc = static_cast<m::MidiChannel&>(c);
- if (mc.isPlaying() && !mc.mute)
- mc.sendMidi(a.next->event, 0);
- });
+ events::sendMidiToChannel(channelId, a.next->event, Thread::MAIN);
mr::deleteAction(a.id, a.next->id);
}
else
namespace cr = c::recorder;
if (a.next != nullptr) // For ChannelMode::SINGLE_PRESS combo
- mr::deleteAction(a.next->id);
- mr::deleteAction(a.id);
+ mr::deleteAction(a.id, a.next->id);
+ else
+ mr::deleteAction(a.id);
recorder::updateChannel(channelId, /*updateActionEditor=*/false);
}
/* 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 (mrh::isBoundaryEnvelopeAction(a)) {
if (a.isVolumeEnvelope()) {
+ /*
m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
{
c.volume_i = 1.0;
c.volume_d = 0.0;
- });
+ });*/
}
mr::clearActions(channelId, a.event.getStatus());
}
mr::updateEvent(a.id, event);
}
-}}}; // giada::c::actionEditor::
+}}} // giada::c::actionEditor::
#define G_GLUE_ACTION_EDITOR_H
+#include <optional>
#include <vector>
+#include <string>
#include "core/types.h"
namespace m
{
struct Action;
-class SampleChannel;
-class MidiChannel;
+class Channel;
+class SamplePlayer;
}
namespace c {
namespace actionEditor
{
-std::vector<m::Action> getActions(ID channelId);
+struct SampleData
+{
+ SampleData(const m::SamplePlayer&);
+
+ SamplePlayerMode channelMode;
+ bool isLoopMode;
+};
+
+struct Data
+{
+ Data() = default;
+ Data(const m::Channel&);
+
+ ID channelId;
+ std::string channelName;
+ std::vector<m::Action> actions;
+
+ std::optional<SampleData> sample;
+};
+
+Data getData(ID channelId);
/* MIDI actions. */
void recordEnvelopeAction(ID channelId, Frame f, int value);
void deleteEnvelopeAction(ID channelId, const m::Action& a);
void updateEnvelopeAction(ID channelId, const m::Action& a, Frame f, int value);
-}}}; // giada::c::actionEditor::
+}}} // giada::c::actionEditor::
#endif
#include "utils/gui.h"
#include "utils/fs.h"
#include "utils/log.h"
-#include "core/channels/channel.h"
-#include "core/channels/sampleChannel.h"
-#include "core/channels/midiChannel.h"
#include "core/model/model.h"
#include "core/kernelAudio.h"
#include "core/mixerHandler.h"
#include "core/mixer.h"
#include "core/clock.h"
-#include "core/pluginHost.h"
+#include "core/plugins/pluginHost.h"
#include "core/conf.h"
#include "core/wave.h"
#include "core/recorder.h"
-#include "core/plugin.h"
+#include "core/recManager.h"
+#include "core/plugins/plugin.h"
#include "core/waveManager.h"
#include "main.h"
#include "channel.h"
else if (res == G_RES_ERR_NO_DATA)
v::gdAlert("No file specified.");
}
+} // {anonymous}
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-void onRefreshSampleEditor_(bool gui, std::function<void(v::gdSampleEditor*)> f)
+SampleData::SampleData(const m::SamplePlayer& s, const m::AudioReceiver& a)
+: waveId (s.getWaveId())
+, mode (s.state->mode.load())
+, isLoop (s.state->isAnyLoopMode())
+, pitch (s.state->pitch.load())
+, m_samplePlayer (&s)
+, m_audioReceiver(&a)
{
- v::gdSampleEditor* gdEditor = static_cast<v::gdSampleEditor*>(u::gui::getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
- if (gdEditor == nullptr)
- return;
- if (!gui) Fl::lock();
- f(gdEditor);
- if (!gui) Fl::unlock();
}
-} // {anonymous}
+Frame SampleData::a_getTracker() const { return a_get(m_samplePlayer->state->tracker); }
+Frame SampleData::a_getBegin() const { return a_get(m_samplePlayer->state->begin); }
+Frame SampleData::a_getEnd() const { return a_get(m_samplePlayer->state->end); }
+bool SampleData::a_getInputMonitor() const { return a_get(m_audioReceiver->state->inputMonitor); }
+bool SampleData::a_getOverdubProtection() const { return a_get(m_audioReceiver->state->overdubProtection); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+MidiData::MidiData(const m::MidiSender& m)
+: m_midiSender(&m)
+{
+}
+
+bool MidiData::a_isOutputEnabled() const { return a_get(m_midiSender->state->enabled); }
+int MidiData::a_getFilter() const { return a_get(m_midiSender->state->filter); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Data::Data(const m::Channel& c)
+: id (c.id)
+, columnId (c.getColumnId())
+#ifdef WITH_VST
+, pluginIds (c.pluginIds)
+#endif
+, type (c.getType())
+, height (c.state->height)
+, name (c.state->name)
+, volume (c.state->volume.load())
+, pan (c.state->pan.load())
+, key (c.state->key.load())
+, hasActions (c.state->hasActions)
+, m_channel (c)
+{
+ if (c.getType() == ChannelType::SAMPLE)
+ sample = std::make_optional<SampleData>(*c.samplePlayer, *c.audioReceiver);
+ else
+ if (c.getType() == ChannelType::MIDI)
+ midi = std::make_optional<MidiData>(*c.midiSender);
+}
+
+
+bool Data::a_getSolo() const { return a_get(m_channel.state->solo); }
+bool Data::a_getMute() const { return a_get(m_channel.state->mute); }
+ChannelStatus Data::a_getPlayStatus() const { return a_get(m_channel.state->playStatus); }
+ChannelStatus Data::a_getRecStatus() const { return a_get(m_channel.state->recStatus); }
+bool Data::a_getReadActions() const { return a_get(m_channel.state->readActions); }
+bool Data::a_isArmed() const { return a_get(m_channel.state->armed); }
+bool Data::a_isRecordingInput() const { return m::recManager::isRecordingInput(); }
+bool Data::a_isRecordingAction() const { return m::recManager::isRecordingAction(); }
+
+
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
+
+
+Data getData(ID channelId)
+{
+ namespace mm = m::model;
+
+ mm::ChannelsLock cl(mm::channels);
+ return Data(mm::get(mm::channels, channelId));
+}
+
+
+std::vector<Data> getChannels()
+{
+ namespace mm = m::model;
+ mm::ChannelsLock cl(mm::channels);
+
+ std::vector<Data> out;
+ for (const m::Channel* ch : mm::channels)
+ if (!ch->isInternal())
+ out.push_back(Data(*ch));
+
+ return out;
+}
+
+
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-void setArm(ID channelId, bool value)
-{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c) { c.armed = value; });
-}
-
-
-void toggleArm(ID channelId)
-{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c) { c.armed = !c.armed; });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
void setInputMonitor(ID channelId, bool value)
{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
+ m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
{
- static_cast<m::SampleChannel&>(c).inputMonitor = value;
+ c.audioReceiver->state->inputMonitor.store(value);
});
}
/* -------------------------------------------------------------------------- */
-void cloneChannel(ID channelId)
-{
- m::mh::cloneChannel(channelId);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setVolume(ID channelId, float value, bool gui, bool editor)
+void setOverdubProtection(ID channelId, bool value)
{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c) { c.volume = value; });
-
- /* Changing channel volume? Update wave editor (if it's shown). */
-
- if (editor)
- onRefreshSampleEditor_(gui, [](v::gdSampleEditor* e) { e->volumeTool->rebuild(); });
-
- if (!gui) {
- Fl::lock();
- G_MainWin->keyboard->getChannel(channelId)->vol->value(value);
- Fl::unlock();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setPitch(ID channelId, float val, bool gui)
-{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- static_cast<m::SampleChannel&>(c).setPitch(val);
- });
-
- onRefreshSampleEditor_(gui, [](v::gdSampleEditor* e) { e->pitchTool->rebuild(); });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setPan(ID channelId, float val, bool gui)
-{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c) { c.setPan(val); });
-
- onRefreshSampleEditor_(gui, [](v::gdSampleEditor* e) { e->panTool->rebuild(); });
+ m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+ {
+ c.audioReceiver->state->overdubProtection.store(value);
+ if (value == true && c.state->armed.load() == true)
+ c.state->armed.store(false);
+ });
}
/* -------------------------------------------------------------------------- */
-void setMute(ID channelId, bool value)
-{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch) { ch.setMute(value); });
-}
-
-
-void toggleMute(ID channelId)
+void cloneChannel(ID channelId)
{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch) { ch.setMute(!ch.mute); });
+ m::mh::cloneChannel(channelId);
}
/* -------------------------------------------------------------------------- */
-void setSampleMode(ID channelId, ChannelMode m)
+void setSamplePlayerMode(ID channelId, SamplePlayerMode m)
{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch)
+ m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
{
- static_cast<m::SampleChannel&>(ch).mode = m;
+ c.samplePlayer->state->mode.store(m);
});
- u::gui::refreshActionEditor();
-}
-
+ /* TODO - brutal rebuild! Just rebuild the specific channel instead */
+ G_MainWin->keyboard->rebuild();
-/* -------------------------------------------------------------------------- */
-
-
-void setSolo(ID channelId, bool value)
-{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch) { ch.setSolo(value); });
- m::mh::updateSoloCount();
-}
-
-
-void toggleSolo(ID channelId)
-{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch) { ch.setSolo(!ch.solo); });
- m::mh::updateSoloCount();
-}
-
-/* -------------------------------------------------------------------------- */
-
-
-void start(ID channelId, int velocity, bool record)
-{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch)
- {
- if (record && !ch.recordStart(m::clock::canQuantize()))
- return;
- ch.start(/*localFrame=*/0, m::clock::canQuantize(), velocity); // Frame 0: user-generated event
- });
+ u::gui::refreshActionEditor();
}
/* -------------------------------------------------------------------------- */
-void kill(ID channelId, bool record)
+void setHeight(ID channelId, Pixel p)
{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch)
- {
- if (record && !ch.recordKill())
- return;
- ch.kill(/*localFrame=*/0); // Frame 0: user-generated event
- });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void stop(ID channelId)
-{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch)
+ m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
{
- ch.recordStop();
- ch.stop();
- });
+ c.state->height = p;
+ });
}
{
m::mh::renameChannel(channelId, name);
}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void toggleReadingActions(ID channelId)
-{
- /* When you call startReadingRecs with conf::treatRecsAsLoops, the
- member value ch->readActions actually is not set to true immediately, because
- the channel is in wait mode (REC_WAITING). ch->readActions will become true on
- the next first beat. So a 'stop rec' command should occur also when
- ch->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. */
-
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch)
- {
- if (!ch.hasActions)
- return;
- if (ch.readActions || (!ch.readActions && ch.recStatus == ChannelStatus::WAIT))
- ch.stopReadingActions(m::clock::isRunning(), m::conf::conf.treatRecsAsLoops,
- m::conf::conf.recsStopOnChanHalt);
- else
- ch.startReadingActions(m::conf::conf.treatRecsAsLoops, m::conf::conf.recsStopOnChanHalt);
- });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void startReadingActions(ID channelId)
-{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch)
- {
- ch.startReadingActions(m::conf::conf.treatRecsAsLoops, m::conf::conf.recsStopOnChanHalt);
- });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void stopReadingActions(ID channelId)
-{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& ch)
- {
- ch.stopReadingActions(m::clock::isRunning(), m::conf::conf.treatRecsAsLoops,
- m::conf::conf.recsStopOnChanHalt);
- });
-}
-
-}}}; // giada::c::channel::
+}}} // giada::c::channel::
- /* -----------------------------------------------------------------------------
+ /* -----------------------------------------------------------------------------
*
* Giada - Your Hardcore Loopmachine
*
#define G_GLUE_CHANNEL_H
+#include <optional>
+#include <atomic>
#include <string>
#include <vector>
+#include "core/model/model.h"
#include "core/types.h"
namespace m
{
class Channel;
+class SamplePlayer;
}
namespace c {
namespace channel
{
+struct SampleData
+{
+ SampleData() = delete;
+ SampleData(const m::SamplePlayer&, const m::AudioReceiver&);
+
+ Frame a_getTracker() const;
+ Frame a_getBegin() const;
+ Frame a_getEnd() const;
+ bool a_getInputMonitor() const;
+ bool a_getOverdubProtection() const;
+
+ ID waveId;
+ SamplePlayerMode mode;
+ bool isLoop;
+ float pitch;
+
+private:
+
+ const m::SamplePlayer* m_samplePlayer;
+ const m::AudioReceiver* m_audioReceiver;
+};
+
+struct MidiData
+{
+ MidiData() = delete;
+ MidiData(const m::MidiSender&);
+
+ bool a_isOutputEnabled() const;
+ int a_getFilter() const;
+
+private:
+
+ const m::MidiSender* m_midiSender;
+};
+
+struct Data
+{
+ Data(const m::Channel&);
+
+ bool a_getMute() const;
+ bool a_getSolo() const;
+ ChannelStatus a_getPlayStatus() const;
+ ChannelStatus a_getRecStatus() const;
+ bool a_getReadActions() const;
+ bool a_isArmed() const;
+ bool a_isRecordingInput() const;
+ bool a_isRecordingAction() const;
+
+ ID id;
+ ID columnId;
+#ifdef WITH_VST
+ std::vector<ID> pluginIds;
+#endif
+ ChannelType type;
+ Pixel height;
+ std::string name;
+ float volume;
+ float pan;
+ int key;
+ bool hasActions;
+
+ std::optional<SampleData> sample;
+ std::optional<MidiData> midi;
+
+private:
+
+ const m::Channel& m_channel;
+};
+
+/* 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<Data> getChannels();
+
+/* a_get
+Returns an atomic property from a Channel, by locking it first. */
+
+template <typename T>
+T a_get(const std::atomic<T>& a)
+{
+ m::model::ChannelsLock l(m::model::channels);
+ return a.load();
+}
+
/* addChannel
Adds an empty new channel to the stack. */
/* loadChannel
Fills an existing channel with a wave. */
-int loadChannel(ID channelId, const std::string& fname);
+int loadChannel(ID columnId, const std::string& fname);
/* addAndLoadChannel
Adds a new Sample Channel and fills it with a wave right away. */
void freeChannel(ID channelId);
/* cloneChannel
-Makes an exact copy of Channel *ch. */
+Makes an exact copy of a channel. */
void cloneChannel(ID channelId);
/* set*
Sets several channel properties. */
-void setArm(ID channelId, bool value);
-void toggleArm(ID channelId);
void setInputMonitor(ID channelId, bool value);
-void setMute(ID channelId, bool value);
-void toggleMute(ID channelId);
-void setSolo(ID channelId, bool value);
-void toggleSolo(ID channelId);
-void setVolume(ID channelId, float v, bool gui=true, bool editor=false);
+void setOverdubProtection(ID channelId, bool value);
void setName(ID channelId, const std::string& name);
-void setPitch(ID channelId, float val, bool gui=true);
-void setPan(ID channelId, float val, bool gui=true);
-void setSampleMode(ID channelId, ChannelMode m);
-
-void start(ID channelId, int velocity, bool record);
-void kill(ID channelId, bool record);
-void stop(ID channelId);
-
-/* toggleReadingRecs
-Handles the 'R' button. If gui == true the signal comes from an user interaction
-on the GUI, otherwise it's a MIDI/Jack/external signal. */
-
-void toggleReadingActions(ID channelId);
-void startReadingActions(ID channelId);
-void stopReadingActions(ID channelId);
+void setHeight(ID channelId, Pixel p);
-}}}; // giada::c::channel::
+void setSamplePlayerMode(ID channelId, SamplePlayerMode m);
+}}} // giada::c::channel::
#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cassert>
+#include <FL/Fl.H>
+#include "core/model/model.h"
+#include "core/const.h"
+#include "core/clock.h"
+#include "core/mixer.h"
+#include "core/midiEvent.h"
+#include "core/plugins/pluginHost.h"
+#include "core/sequencer.h"
+#include "core/mixerHandler.h"
+#include "core/conf.h"
+#include "core/recManager.h"
+#include "utils/log.h"
+#include "gui/dialogs/sampleEditor.h"
+#include "gui/dialogs/mainWindow.h"
+#include "gui/dialogs/warnings.h"
+#include "gui/elems/mainWindow/mainIO.h"
+#include "gui/elems/mainWindow/mainTimer.h"
+#include "gui/elems/basics/dial.h"
+#include "gui/elems/mainWindow/keyboard/keyboard.h"
+#include "gui/elems/mainWindow/keyboard/channel.h"
+#include "gui/elems/sampleEditor/volumeTool.h"
+#include "gui/elems/sampleEditor/pitchTool.h"
+#include "gui/elems/sampleEditor/panTool.h"
+#include "glue/sampleEditor.h"
+#include "glue/plugin.h"
+#include "glue/main.h"
+#include "events.h"
+
+
+extern giada::v::gdMainWindow* G_MainWin;
+
+
+namespace giada {
+namespace c {
+namespace events
+{
+namespace
+{
+void pushEvent_(m::mixer::Event e, Thread t)
+{
+ bool res = true;
+ if (t == Thread::MAIN)
+ res = m::mixer::UIevents.push(e);
+ else
+ if (t == Thread::MIDI)
+ res = m::mixer::MidiEvents.push(e);
+ else
+ assert(false);
+
+ if (!res)
+ G_DEBUG("[events] Queue full!\n");
+}
+} // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+void pressChannel(ID channelId, int velocity, Thread t)
+{
+ m::MidiEvent e;
+ e.setVelocity(velocity);
+ pushEvent_({ m::mixer::EventType::KEY_PRESS, 0, {0, channelId, 0, e} }, t);
+}
+
+
+void releaseChannel(ID channelId, Thread t)
+{
+ pushEvent_({ m::mixer::EventType::KEY_RELEASE, 0, {0, channelId} }, t);
+}
+
+
+void killChannel(ID channelId, Thread t)
+{
+ pushEvent_({ m::mixer::EventType::KEY_KILL, 0, {0, channelId} }, t);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void setChannelVolume(ID channelId, float v, Thread t)
+{
+ v = std::clamp(v, 0.0f, G_MAX_VOLUME);
+
+ pushEvent_({ m::mixer::EventType::CHANNEL_VOLUME, 0, { 0, channelId, 0, {v} } }, t);
+
+ sampleEditor::onRefresh(t == Thread::MAIN, [v](v::gdSampleEditor& e) { e.volumeTool->update(v); });
+
+ if (t != Thread::MAIN) {
+ Fl::lock();
+ G_MainWin->keyboard->getChannel(channelId)->vol->value(v);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void setChannelPitch(ID channelId, float v, Thread t)
+{
+ v = std::clamp(v, G_MIN_PITCH, G_MAX_PITCH);
+
+ pushEvent_({ m::mixer::EventType::CHANNEL_PITCH, 0, { 0, channelId, 0, {v} } }, t);
+
+ sampleEditor::onRefresh(t == Thread::MAIN, [v](v::gdSampleEditor& e) { e.pitchTool->update(v); });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void sendChannelPan(ID channelId, float v)
+{
+ v = std::clamp(v, 0.0f, G_MAX_PAN);
+
+ /* Pan event is currently triggered only by the main thread. */
+ pushEvent_({ m::mixer::EventType::CHANNEL_PAN, 0, { 0, channelId, 0, {v} } }, Thread::MAIN);
+
+ sampleEditor::onRefresh(/*gui=*/true, [v](v::gdSampleEditor& e) { e.panTool->update(v); });
+}
+
+/* -------------------------------------------------------------------------- */
+
+
+void toggleMuteChannel(ID channelId, Thread t)
+{
+ pushEvent_({ m::mixer::EventType::CHANNEL_MUTE, 0, {0, channelId} }, t);
+}
+
+
+void toggleSoloChannel(ID channelId, Thread t)
+{
+ pushEvent_({ m::mixer::EventType::CHANNEL_SOLO, 0, {0, channelId} }, t);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void toggleArmChannel(ID channelId, Thread t)
+{
+ pushEvent_({ m::mixer::EventType::CHANNEL_TOGGLE_ARM, 0, {0, channelId} }, t);
+}
+
+
+void toggleReadActionsChannel(ID channelId, Thread t)
+{
+ pushEvent_({ m::mixer::EventType::CHANNEL_TOGGLE_READ_ACTIONS, 0, {0, channelId} }, t);
+}
+
+
+void killReadActionsChannel(ID channelId, Thread t)
+{
+ pushEvent_({ m::mixer::EventType::CHANNEL_KILL_READ_ACTIONS, 0, {0, channelId} }, t);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void sendMidiToChannel(ID channelId, m::MidiEvent e, Thread t)
+{
+ pushEvent_({ m::mixer::EventType::MIDI, 0, {0, channelId, 0, e} }, t);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void toggleMetronome()
+{
+ m::sequencer::toggleMetronome();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void setMasterInVolume(float v, Thread t)
+{
+ pushEvent_({ m::mixer::EventType::CHANNEL_VOLUME, 0, { 0, m::mixer::MASTER_IN_CHANNEL_ID, 0, {v} }}, t);
+
+ if (t != Thread::MAIN) {
+ Fl::lock();
+ G_MainWin->mainIO->setInVol(v);
+ Fl::unlock();
+ }
+}
+
+
+void setMasterOutVolume(float v, Thread t)
+{
+ pushEvent_({ m::mixer::EventType::CHANNEL_VOLUME, 0, { 0, m::mixer::MASTER_OUT_CHANNEL_ID, 0, {v} }}, t);
+
+ if (t != Thread::MAIN) {
+ Fl::lock();
+ G_MainWin->mainIO->setOutVol(v);
+ Fl::unlock();
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void multiplyBeats()
+{
+ main::setBeats(m::clock::getBeats() * 2, m::clock::getBars());
+}
+
+
+void divideBeats()
+{
+ main::setBeats(m::clock::getBeats() / 2, m::clock::getBars());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void startSequencer(Thread t)
+{
+ pushEvent_({ m::mixer::EventType::SEQUENCER_START, 0 }, t);
+ m::conf::conf.recTriggerMode = RecTriggerMode::NORMAL;
+}
+
+
+void stopSequencer(Thread t)
+{
+ pushEvent_({ m::mixer::EventType::SEQUENCER_STOP, 0 }, t);
+}
+
+
+void toggleSequencer(Thread t)
+{
+ m::clock::isRunning() ? stopSequencer(t) : startSequencer(t);
+}
+
+
+void rewindSequencer(Thread t)
+{
+ pushEvent_({ m::mixer::EventType::SEQUENCER_REWIND_REQ, 0 }, t);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void toggleActionRecording()
+{
+ m::recManager::toggleActionRec(m::conf::conf.recTriggerMode);
+}
+
+
+void toggleInputRecording()
+{
+ m::recManager::toggleInputRec(m::conf::conf.recTriggerMode);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+#ifdef WITH_VST
+void setPluginParameter(ID pluginId, int paramIndex, float value, bool gui)
+{
+ m::pluginHost::setPluginParameter(pluginId, paramIndex, value);
+ c::plugin::updateWindow(pluginId, gui);
+}
+#endif
+}}} // giada::c::events::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef G_GLUE_EVENTS_H
+#define G_GLUE_EVENTS_H
+
+
+#include "core/types.h"
+
+
+/* giada::c::events
+Functions that take care of live event dispatching. Every live gesture that
+comes from the UI, MIDI thread or keyboard interaction and wants to change the
+internal engine state must call these functions. */
+
+namespace giada {
+namespace m
+{
+class MidiEvent;
+}
+namespace c {
+namespace 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 toggleActionRecording();
+void toggleInputRecording ();
+
+/* Plug-ins. */
+
+#ifdef WITH_VST
+void setPluginParameter(ID pluginId, int paramIndex, float value, bool gui);
+#endif
+}}} // giada::c::events::
+
+
+#endif
#include "utils/log.h"
#include "utils/math.h"
#include "core/model/model.h"
-#include "core/channels/sampleChannel.h"
-#include "core/channels/channel.h"
-#include "core/channels/midiChannel.h"
#include "core/recorder.h"
#include "core/conf.h"
#include "core/recManager.h"
{
namespace
{
-void refreshMidiWindows_()
+void rebuildMidiWindows_()
{
- Fl::lock();
- u::gui::refreshSubWindow(WID_MIDI_INPUT);
- u::gui::refreshSubWindow(WID_MIDI_OUTPUT);
- Fl::unlock();
+ u::gui::rebuildSubWindow(WID_MIDI_INPUT);
+ u::gui::rebuildSubWindow(WID_MIDI_OUTPUT);
}
} // {anonymous}
/* -------------------------------------------------------------------------- */
-void keyPress(ID channelId, bool ctrl, bool shift, int velocity)
+Channel_InputData::Channel_InputData(const m::Channel& c)
+: channelId (c.id)
+, channelType (c.getType())
+, enabled (c.midiLearner.state->enabled.load())
+, velocityAsVol(c.samplePlayer ? c.samplePlayer->state->velocityAsVol.load() : 0)
+, filter (c.midiLearner.state->filter.load())
+, keyPress (c.midiLearner.state->keyPress.getValue())
+, keyRelease (c.midiLearner.state->keyRelease.getValue())
+, kill (c.midiLearner.state->kill.getValue())
+, arm (c.midiLearner.state->arm.getValue())
+, volume (c.midiLearner.state->volume.getValue())
+, mute (c.midiLearner.state->mute.getValue())
+, solo (c.midiLearner.state->solo.getValue())
+, pitch (c.midiLearner.state->pitch.getValue())
+, readActions (c.midiLearner.state->readActions.getValue())
{
- if (ctrl)
- c::channel::toggleMute(channelId);
- else
- if (shift)
- c::channel::kill(channelId, /*record=*/true);
- else
- c::channel::start(channelId, velocity, /*record=*/true);
+#ifdef WITH_VST
+ for (ID id : c.pluginIds) {
+ m::Plugin& p = m::model::get(m::model::plugins, id);
+
+ 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.state->enabled.load())
+, filter (s.state->filter.load())
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Channel_OutputData::Channel_OutputData(const m::Channel& c)
+: channelId (c.id)
+, lightningEnabled(c.midiLighter.state->enabled.load())
+, lightningPlaying(c.midiLighter.state->playing.getValue())
+, lightningMute (c.midiLighter.state->mute.getValue())
+, lightningSolo (c.midiLighter.state->solo.getValue())
+{
+ if (c.getType() == ChannelType::MIDI)
+ output = std::make_optional<MidiChannel_OutputData>(*c.midiSender);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Master_InputData::Master_InputData(const m::model::MidiIn& midiIn)
+: enabled (midiIn.enabled)
+, filter (midiIn.filter)
+, rewind (midiIn.rewind)
+, startStop (midiIn.startStop)
+, actionRec (midiIn.actionRec)
+, inputRec (midiIn.inputRec)
+, volumeIn (midiIn.volumeIn)
+, volumeOut (midiIn.volumeOut)
+, beatDouble(midiIn.beatDouble)
+, beatHalf (midiIn.beatHalf)
+, metronome (midiIn.metronome)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+Channel_InputData channel_getInputData(ID channelId)
+{
+ namespace mm = m::model;
+
+ mm::ChannelsLock cl(mm::channels);
+#ifdef WITH_VST
+ mm::PluginsLock ml(mm::plugins);
+#endif
+
+ return Channel_InputData(mm::get(mm::channels, channelId));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Channel_OutputData channel_getOutputData(ID channelId)
+{
+ namespace mm = m::model;
+
+ mm::ChannelsLock cl(mm::channels);
+ return Channel_OutputData(mm::get(mm::channels, channelId));
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+Master_InputData master_getInputData()
+{
+ namespace mm = m::model;
+
+ mm::MidiInLock l(mm::midiIn);
+ return Master_InputData(*mm::midiIn.get());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void channel_enableMidiLearn(ID channelId, bool v)
+{
+ m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+ {
+ c.midiLearner.state->enabled.store(v);
+ });
+ rebuildMidiWindows_();
}
/* -------------------------------------------------------------------------- */
-void keyRelease(ID channelId, bool ctrl, bool shift)
+void channel_enableMidiLightning(ID channelId, bool v)
{
- if (!ctrl && !shift)
- c::channel::stop(channelId);
+ m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+ {
+ c.midiLighter.state->enabled.store(v);
+ });
+ rebuildMidiWindows_();
}
/* -------------------------------------------------------------------------- */
-void setSampleChannelKey(ID channelId, int k)
+void channel_enableMidiOutput(ID channelId, bool v)
{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
+ m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
{
- c.key = k;
+ c.midiSender->state->enabled.store(v);
+ });
+ rebuildMidiWindows_();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void channel_enableVelocityAsVol(ID channelId, bool v)
+{
+ m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+ {
+ c.samplePlayer->state->velocityAsVol.store(v);
});
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void channel_setMidiInputFilter(ID channelId, int ch)
+{
+ m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+ {
+ c.midiLearner.state->filter.store(ch);
+ });
+}
+
- Fl::lock();
- G_MainWin->keyboard->getChannel(channelId)->mainButton->setKey(k);
- Fl::unlock();
+void channel_setMidiOutputFilter(ID channelId, int ch)
+{
+ m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+ {
+ c.midiSender->state->filter.store(ch);
+ });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void channel_setKey(ID channelId, int k)
+{
+ m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+ {
+ c.state->key.store(k);
+ }, /*rebuild=*/true);
}
/* -------------------------------------------------------------------------- */
-void startChannelMidiLearn(int param, ID channelId)
+void channel_startMidiLearn(int param, ID channelId)
{
- m::midiDispatcher::startChannelLearn(param, channelId, refreshMidiWindows_);
+ m::midiDispatcher::startChannelLearn(param, channelId, rebuildMidiWindows_);
}
-void startMasterMidiLearn(int param)
+void master_startMidiLearn(int param)
{
- m::midiDispatcher::startMasterLearn(param, refreshMidiWindows_);
+ m::midiDispatcher::startMasterLearn(param, rebuildMidiWindows_);
}
#ifdef WITH_VST
-void startPluginMidiLearn(int paramIndex, ID pluginId)
+void plugin_startMidiLearn(int paramIndex, ID pluginId)
{
- m::midiDispatcher::startPluginLearn(paramIndex, pluginId, refreshMidiWindows_);
+ m::midiDispatcher::startPluginLearn(paramIndex, pluginId, rebuildMidiWindows_);
}
#endif
void stopMidiLearn()
{
m::midiDispatcher::stopLearn();
- refreshMidiWindows_();
+ rebuildMidiWindows_();
}
/* -------------------------------------------------------------------------- */
-void clearChannelMidiLearn(int param, ID channelId)
+void channel_clearMidiLearn(int param, ID channelId)
{
- m::midiDispatcher::clearChannelLearn(param, channelId, refreshMidiWindows_);
+ m::midiDispatcher::clearChannelLearn(param, channelId, rebuildMidiWindows_);
}
-void clearMasterMidiLearn (int param)
+void master_clearMidiLearn (int param)
{
- m::midiDispatcher::clearMasterLearn(param, refreshMidiWindows_);
+ m::midiDispatcher::clearMasterLearn(param, rebuildMidiWindows_);
}
#ifdef WITH_VST
-void clearPluginMidiLearn (int param, ID pluginId)
+void plugin_clearMidiLearn (int param, ID pluginId)
{
- m::midiDispatcher::clearPluginLearn(param, pluginId, refreshMidiWindows_);
+ m::midiDispatcher::clearPluginLearn(param, pluginId, rebuildMidiWindows_);
}
#endif
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void master_enableMidiLearn(bool v)
+{
+ m::model::onSwap(m::model::midiIn, [&](m::model::MidiIn& m)
+ {
+ m.enabled = v;
+ });
+ rebuildMidiWindows_();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void master_setMidiFilter(int c)
+{
+ m::model::onSwap(m::model::midiIn, [&](m::model::MidiIn& m)
+ {
+ m.filter = c;
+ });
+}
}}} // giada::c::io::
namespace c {
namespace io
{
-/* keyPress / keyRelease
-Handle the key pressure, either via mouse/keyboard or MIDI. */
+struct PluginParamData
+{
+ int index;
+ std::string name;
+ uint32_t value;
+};
+
+struct PluginData
+{
+ ID id;
+ std::string name;
+ std::vector<PluginParamData> params;
+};
+
+struct Channel_InputData
+{
+ Channel_InputData() = default;
+ Channel_InputData(const m::Channel&);
+
+ ID channelId;
+ ChannelType channelType;
+ bool enabled;
+ bool velocityAsVol;
+ int filter;
+
+ uint32_t keyPress;
+ uint32_t keyRelease;
+ uint32_t kill;
+ uint32_t arm;
+ uint32_t volume;
+ uint32_t mute;
+ uint32_t solo;
+ uint32_t pitch;
+ uint32_t readActions;
+
+ std::vector<PluginData> plugins;
+};
+
+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;
+};
-void keyPress (ID channelId, bool ctrl, bool shift, int velocity);
-void keyRelease(ID channelId, bool ctrl, bool shift);
+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<MidiChannel_OutputData> output;
+};
-/* setSampleChannelKey
+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. */
-void setSampleChannelKey(ID channelId, int k);
+void channel_setKey(ID channelId, int k);
-void startChannelMidiLearn(int param, ID channelId);
-void startMasterMidiLearn (int param);
+/* 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();
-void clearChannelMidiLearn(int param, ID channelId);
-void clearMasterMidiLearn (int param);
#ifdef WITH_VST
-void startPluginMidiLearn (int paramIndex, ID pluginId);
-void clearPluginMidiLearn (int param, ID pluginId);
+void plugin_startMidiLearn (int paramIndex, ID pluginId);
+void plugin_clearMidiLearn (int param, ID pluginId);
#endif
+
+/* Master functions. */
+
+void master_enableMidiLearn(bool v);
+void master_setMidiFilter(int c);
}}} // giada::c::io::
#endif
#include "utils/string.h"
#include "utils/log.h"
#include "core/model/model.h"
-#include "core/channels/midiChannel.h"
#include "core/mixerHandler.h"
#include "core/mixer.h"
#include "core/clock.h"
#include "core/recManager.h"
#include "core/conf.h"
#include "core/const.h"
-#include "core/pluginManager.h"
-#include "core/pluginHost.h"
+#include "core/plugins/pluginManager.h"
+#include "core/plugins/pluginHost.h"
#include "main.h"
float previous = m::clock::getBpm();
m::clock::setBpm(current);
- m::recorderHandler::updateBpm(previous, current, m::clock::getQuanto());
- m::mixer::allocVirtualInput(m::clock::getFramesInLoop());
+ m::recorderHandler::updateBpm(previous, current, m::clock::getQuantizerStep());
+ m::mixer::allocRecBuffer(m::clock::getFramesInLoop());
/* This function might get called by Jack callback BEFORE the UI is up
and running, that is when G_MainWin == nullptr. */
G_MainWin->mainTimer->setBpm(s.c_str());
}
- u::log::print("[glue::setBpm_] Bpm changed to %s (real=%f)\n", s.c_str(), m::clock::getBpm());
+ u::log::print("[glue::setBpm_] Bpm changed to %s (real=%f)\n", s, m::clock::getBpm());
}
} // {anonymous}
/* -------------------------------------------------------------------------- */
+Timer::Timer(const m::model::Clock& c)
+: bpm (c.bpm)
+, beats (c.beats)
+, bars (c.bars)
+, quantize (c.quantize)
+, isUsingJack (m::kernelAudio::getAPI() == G_SYS_API_JACK)
+, isRecordingInput(m::recManager::isRecordingInput())
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+IO::IO(const m::Channel& out, const m::Channel& in, const m::model::Mixer& m)
+: masterOutVol (out.state->volume.load())
+, masterInVol (in.state->volume.load())
+#ifdef WITH_VST
+, masterOutHasPlugins(out.pluginIds.size() > 0)
+, masterInHasPlugins (in.pluginIds.size() > 0)
+#endif
+, inToOut (m.inToOut)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+float IO::a_getMasterOutPeak()
+{
+ return m::mixer::peakOut.load();
+}
+
+
+float IO::a_getMasterInPeak()
+{
+ return m::mixer::peakIn.load();
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+Timer getTimer()
+{
+ namespace mm = m::model;
+
+ mm::ClockLock c(mm::clock);
+ return Timer(*mm::clock.get());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+IO getIO()
+{
+ namespace mm = m::model;
+
+ mm::ChannelsLock cl(mm::channels);
+ mm::MixerLock ml(mm::mixer);
+
+ return IO(mm::get(mm::channels, m::mixer::MASTER_OUT_CHANNEL_ID),
+ mm::get(mm::channels, m::mixer::MASTER_IN_CHANNEL_ID),
+ *mm::mixer.get());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
void setBpm(const char* v1, const char* v2)
{
/* Never change this stuff while recording audio. */
the nice looking (but fake) one to the GUI.
On Linux, let Jack handle the bpm change if it's on. */
- float f = std::atof(v1) + (std::atof(v2)/10);
+ float f = static_cast<float>(std::atof(v1) + (std::atof(v2)/10));
std::string s = std::string(v1) + "." + std::string(v2);
-#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
+#ifdef WITH_AUDIO_JACK
if (m::kernelAudio::getAPI() == G_SYS_API_JACK)
m::kernelAudio::jackSetBpm(f);
else
return;
m::clock::setBeats(beats, bars);
- m::mixer::allocVirtualInput(m::clock::getFramesInLoop());
+ m::mixer::allocRecBuffer(m::clock::getFramesInLoop());
G_MainWin->mainTimer->setMeter(m::clock::getBeats(), m::clock::getBars());
u::gui::refreshActionEditor(); // in case the action editor is open
/* -------------------------------------------------------------------------- */
-void setOutVol(float v, bool gui)
-{
- m::mh::setOutVol(v);
-
- if (!gui) {
- Fl::lock();
- G_MainWin->mainIO->setOutVol(v);
- Fl::unlock();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setInVol(float v, bool gui)
-{
- m::mh::setInVol(v);
-
- if (!gui) {
- Fl::lock();
- G_MainWin->mainIO->setInVol(v);
- Fl::unlock();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
void clearAllSamples()
{
- if (!v::gdConfirmWin("Warning", "Clear all samples: are you sure?"))
+ if (!v::gdConfirmWin("Warning", "Free all Sample channels: are you sure?"))
return;
G_MainWin->delSubWindow(WID_SAMPLE_EDITOR);
m::clock::setStatus(ClockStatus::STOPPED);
/* -------------------------------------------------------------------------- */
-void resetToInitState(bool createColumns)
+void setInToOut(bool v)
{
- if (!v::gdConfirmWin("Warning", "Close project: are you sure?"))
- return;
- m::init::reset();
- m::mixer::enable();
+ m::mh::setInToOut(v);
}
/* -------------------------------------------------------------------------- */
-void beatsMultiply()
+void toggleRecOnSignal()
{
- setBeats(m::clock::getBeats() * 2, m::clock::getBars());
-}
-
-void beatsDivide()
-{
- setBeats(m::clock::getBeats() / 2, m::clock::getBars());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void toggleInputRec()
-{
- if (!m::recManager::toggleInputRec(static_cast<RecTriggerMode>(m::conf::conf.recTriggerMode)))
- v::gdAlert("No channels armed/available for audio recording.");
+ /* Can't set RecTriggerMode::SIGNAL while sequencer is running, in order
+ to prevent mistakes while live recording. */
+
+ if (m::conf::conf.recTriggerMode == RecTriggerMode::NORMAL && m::clock::isRunning())
+ return;
+ m::conf::conf.recTriggerMode = m::conf::conf.recTriggerMode == RecTriggerMode::NORMAL ? RecTriggerMode::SIGNAL : RecTriggerMode::NORMAL;
}
/* -------------------------------------------------------------------------- */
-void toggleActionRec()
-{
- m::recManager::isRecordingAction() ? stopActionRec() : startActionRec();
-}
-
-
-void startActionRec()
+void closeProject()
{
- m::recManager::startActionRec(static_cast<RecTriggerMode>(m::conf::conf.recTriggerMode));
-}
-
-
-void stopActionRec()
-{
- m::recManager::stopActionRec();
- u::gui::refreshActionEditor(); // If Action Editor window is open
+ if (!v::gdConfirmWin("Warning", "Close project: are you sure?"))
+ return;
+ m::init::reset();
+ m::mixer::enable();
}
-
}}} // giada::c::main::
#define G_MAIN_H
+#include "core/types.h"
+
+
namespace giada {
+namespace m
+{
+class Channel;
+namespace model
+{
+struct Clock;
+struct Mixer;
+}}
namespace c {
namespace main
{
+struct Timer
+{
+ Timer() = default;
+ Timer(const m::model::Clock& c);
+
+ float bpm;
+ int beats;
+ int bars;
+ int quantize;
+ bool isUsingJack;
+ bool isRecordingInput;
+};
+
+struct IO
+{
+ IO() = default;
+ IO(const m::Channel& out, const m::Channel& in, const m::model::Mixer& m);
+
+ float masterOutVol;
+ float masterInVol;
+#ifdef WITH_VST
+ bool masterOutHasPlugins;
+ bool masterInHasPlugins;
+#endif
+ bool inToOut;
+
+ float a_getMasterOutPeak();
+ float a_getMasterInPeak();
+};
+
+/* get*
+Returns viewModel objects filled with data. */
+
+Timer getTimer();
+IO getIO();
+
/* setBpm (1)
Sets bpm value from string to float. */
void setBeats(int beats, int bars);
void quantize(int val);
-void setOutVol(float v, bool gui=true);
-void setInVol(float v, bool gui=true);
void clearAllSamples();
void clearAllActions();
-/* resetToInitState
-Resets Giada to init state. If resetGui also refresh all widgets. If
-createColumns also build initial empty columns. */
-
-void resetToInitState(bool createColumns);
-
-/* beatsDivide/Multiply
-Shrinks or enlarges the number of beats by 2. */
-
-void beatsMultiply();
-void beatsDivide();
-
-
-
-
-
-
-
-
-
-void rewind();
-void play();
-
-/* toggleInputRec
-Handles the input recording.*/
-
-void toggleInputRec();
+/* setInToOut
+Enables the "hear what you playing" feature. */
-void toggleActionRec();
-void startActionRec();
-void stopActionRec();
+void setInToOut(bool v);
+void toggleRecOnSignal();
-void toggleMetronome();
+/* closeProject
+Resets Giada to init state. If resetGui also refresh all widgets. */
+void closeProject();
}}} // giada::c::main::
#endif
#include <cassert>
#include <FL/Fl.H>
#include "core/model/model.h"
-#include "core/channels/channel.h"
-#include "core/pluginManager.h"
-#include "core/pluginHost.h"
+#include "core/plugins/pluginManager.h"
+#include "core/plugins/pluginHost.h"
#include "core/mixer.h"
-#include "core/plugin.h"
+#include "core/plugins/plugin.h"
#include "core/const.h"
#include "core/conf.h"
#include "utils/gui.h"
namespace c {
namespace plugin
{
-namespace
+Param::Param(const m::Plugin& p, int index)
+: index (index)
+, pluginId(p.id)
+, name (p.getParameterName(index))
+, text (p.getParameterText(index))
+, label (p.getParameterLabel(index))
+, value (p.getParameter(index))
{
-void updatePluginEditor_(ID pluginId, bool gui)
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+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
+{
+ m::model::PluginsLock l(m::model::plugins);
+ return m_plugin.createEditor();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void Plugin::setResizeCallback(std::function<void(int, int)> f)
+{
+ m_plugin.onEditorResize = f;
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+Plugins::Plugins(const m::Channel& c)
+: channelId(c.id)
+, pluginIds(c.pluginIds)
+{
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+Plugins getPlugins(ID channelId)
+{
+ namespace mm = m::model;
+
+ mm::ChannelsLock cl(mm::channels);
+ return Plugins(mm::get(mm::channels, channelId));
+}
+
+
+Plugin getPlugin(ID pluginId, ID channelId)
+{
+ m::model::PluginsLock l(m::model::plugins);
+ return Plugin(m::model::get(m::model::plugins, pluginId), channelId);
+}
+
+
+Param getParam (int index, ID pluginId)
+{
+ m::model::PluginsLock l(m::model::plugins);
+ return Param(m::model::get(m::model::plugins, pluginId), index);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void updateWindow(ID pluginId, bool gui)
{
m::model::PluginsLock l(m::model::plugins);
const m::Plugin& p = m::model::get(m::model::plugins, pluginId);
child->updateParameters(!gui);
if (!gui) Fl::unlock();
}
-} // {anonymous}
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
void setProgram(ID pluginId, int programIndex)
{
m::pluginHost::setPluginProgram(pluginId, programIndex);
- updatePluginEditor_(pluginId, /*gui=*/true);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void setParameter(ID pluginId, int paramIndex, float value, bool gui)
-{
- m::pluginHost::setPluginParameter(pluginId, paramIndex, value);
- updatePluginEditor_(pluginId, gui);
+ updateWindow(pluginId, /*gui=*/true);
}
configWin->refreshVstPath();
}
-}}}; // giada::c::plugin::
+}}} // giada::c::plugin::
#endif
#ifdef WITH_VST
-#include "core/pluginHost.h"
+#include <vector>
+#include <string>
+#include "core/plugins/pluginHost.h"
#include "core/types.h"
+namespace juce {
+class AudioProcessorEditor;
+}
+
+
namespace giada {
namespace m
{
namespace c {
namespace plugin
{
-void addPlugin(int pluginListIndex, ID channelId);
+struct Program
+{
+ int index;
+ std::string name;
+};
-void swapPlugins(ID pluginId1, ID pluginId2, ID channelId);
+struct Param
+{
+ Param() = default;
+ Param(const m::Plugin&, int index);
+
+ int index;
+ ID pluginId;
+ std::string name;
+ std::string text;
+ std::string label;
+ float value;
+};
+
+struct Plugin
+{
+ Plugin(m::Plugin&, ID channelId);
-void freePlugin(ID pluginId, ID channelId);
+ juce::AudioProcessorEditor* createEditor() const;
-void setParameter(ID pluginId, int paramIndex, float value, bool gui=true);
+ void setResizeCallback(std::function<void(int, int)> f);
-void setProgram(ID pluginId, int programIndex);
+ ID id;
+ ID channelId;
+ bool valid;
+ bool hasEditor;
+ bool isBypassed;
+ std::string name;
+ std::string uniqueId;
+ int currentProgram;
+
+ std::vector<Program> programs;
+ std::vector<int> paramIndexes;
+
+private:
+ m::Plugin& m_plugin;
+};
+
+struct Plugins
+{
+ Plugins() = default;
+ Plugins(const m::Channel&);
+
+ ID channelId;
+ std::vector<ID> pluginIds;
+};
+
+/* get*
+Returns ViewModel objects. */
+
+Plugins getPlugins(ID channelId);
+Plugin getPlugin (ID pluginId, ID channelId);
+Param getParam (int index, ID pluginId);
+
+/* updateWindow
+Updates the editor-less plug-in window. This is useless if the plug-in has an
+editor. */
+
+void updateWindow(ID pluginId, bool gui);
+
+void addPlugin(int pluginListIndex, ID channelId);
+void swapPlugins(ID pluginId1, ID pluginId2, ID channelId);
+void freePlugin(ID pluginId, ID channelId);
+void setProgram(ID pluginId, int programIndex);
void toggleBypass(ID pluginId);
/* setPluginPathCb
configuration window. */
void setPluginPathCb(void* data);
-}}}; // giada::c::plugin::
+}}} // giada::c::plugin::
#endif
#include "gui/elems/mainWindow/keyboard/channel.h"
#include "gui/elems/mainWindow/keyboard/sampleChannel.h"
#include "core/channels/channel.h"
-#include "core/channels/sampleChannel.h"
-#include "core/channels/midiChannel.h"
#include "core/const.h"
#include "core/clock.h"
#include "core/model/model.h"
void updateChannel(ID channelId, bool updateActionEditor)
{
- /* TODO - optimization needed. This functions swaps a channel only to set a
- boolean flag. Query the channel first and swap it only if the flag has
- actually changed. */
-
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
+ m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
{
- c.hasActions = m::recorder::hasActions(channelId);
+ c.state->hasActions = m::recorder::hasActions(channelId);
});
if (updateActionEditor)
#include <cassert>
#include <FL/Fl.H>
+#include "glue/events.h"
#include "gui/dialogs/mainWindow.h"
#include "gui/dialogs/sampleEditor.h"
#include "gui/dialogs/warnings.h"
#include "gui/elems/mainWindow/keyboard/keyboard.h"
#include "gui/elems/mainWindow/keyboard/channel.h"
#include "core/model/model.h"
-#include "core/channels/sampleChannel.h"
-#include "core/waveFx.h"
#include "core/wave.h"
#include "core/waveManager.h"
#include "core/mixerHandler.h"
std::unique_ptr<m::Wave> waveBuffer_;
+Frame previewTracker_ = 0;
+
/* -------------------------------------------------------------------------- */
void resetBeginEnd_(ID channelId)
{
- Frame begin;
- Frame end;
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+ m::model::onGet(m::model::channels, channelId, [&](const m::Channel& c)
+ {
+ Frame begin = c.samplePlayer->state->begin.load();
+ Frame end = c.samplePlayer->state->end.load();
+ setBeginEnd(channelId, begin, end);
+ });
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+/* updateWavePtr_
+Updates the Wave pointer in Channel::WaveReader. */
+
+void updateWavePtr_(ID channelId, ID waveId)
+{
+ namespace mm = m::model;
+
+ mm::WavesLock wl(mm::waves);
+ const m::Wave& wave = mm::get(mm::waves, waveId);
+
+ mm::onSwap(mm::channels, channelId, [&](m::Channel& c)
{
- begin = static_cast<m::SampleChannel&>(c).begin;
- end = static_cast<m::SampleChannel&>(c).end;
+ c.samplePlayer->loadWave(&wave);
});
+}
+} // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+Data::Data(const m::Channel& c, const m::Wave& w)
+: channelId (c.id)
+, waveId (w.id)
+, name (c.state->name)
+, volume (c.state->volume.load())
+, pan (c.state->pan.load())
+, pitch (c.samplePlayer->state->pitch.load())
+, begin (c.samplePlayer->state->begin.load())
+, end (c.samplePlayer->state->end.load())
+, shift (c.samplePlayer->state->shift.load())
+, waveSize (w.getSize())
+, waveBits (w.getBits())
+, waveDuration(w.getDuration())
+, waveRate (w.getRate())
+, wavePath (w.getPath())
+, isLogical (w.isLogical())
+{
+}
+
+/* TODO - use c::channel::a_get() */
+ChannelStatus Data::a_getPreviewStatus() const
+{
+ namespace mm = m::model;
+
+ mm::ChannelsLock l(mm::channels);
+ return mm::get(mm::channels, m::mixer::PREVIEW_CHANNEL_ID).state->playStatus.load();
+}
+
+/* TODO - use c::channel::a_get() */
+Frame Data::a_getPreviewTracker() const
+{
+ namespace mm = m::model;
- setBeginEnd(channelId, begin, end);
+ mm::ChannelsLock l(mm::channels);
+ return mm::get(mm::channels, m::mixer::PREVIEW_CHANNEL_ID).samplePlayer->state->tracker.load();
}
-}; // {anonymous}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
+Data getData(ID channelId)
+{
+ namespace mm = m::model;
+
+ mm::ChannelsLock cl(mm::channels);
+ mm::WavesLock wl(mm::waves);
+
+ const m::Channel& channel = mm::get(mm::channels, channelId);
+ const m::Wave& wave = mm::get(mm::waves, channel.samplePlayer->getWaveId());
+
+ /* Prepare the preview channel. */
+
+ m::Channel& preview = mm::get(mm::channels, m::mixer::PREVIEW_CHANNEL_ID);
+ preview.samplePlayer->loadWave(&wave);
+
+ return Data(channel, wave);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void onRefresh(bool gui, std::function<void(v::gdSampleEditor&)> f)
+{
+ v::gdSampleEditor* se = static_cast<v::gdSampleEditor*>(u::gui::getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
+ if (se == nullptr)
+ return;
+ if (!gui) Fl::lock();
+ f(*se);
+ if (!gui) Fl::unlock();
+}
+
+
v::gdSampleEditor* getSampleEditorWindow()
{
v::gdSampleEditor* se = static_cast<v::gdSampleEditor*>(u::gui::getSubwindow(G_MainWin, WID_SAMPLE_EDITOR));
void setBeginEnd(ID channelId, int b, int e)
{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
+ m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
{
- static_cast<m::SampleChannel&>(c).setBegin(b);
- static_cast<m::SampleChannel&>(c).setEnd(e);
+ 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;
+
+ c.samplePlayer->state->begin.store(b);
+ c.samplePlayer->state->end.store(e);
+ if (c.samplePlayer->state->tracker.load() < b)
+ c.samplePlayer->state->tracker.store(b);
});
+ /* TODO waveform widget is dumb and wants a rebuild. Refactoring needed! */
getSampleEditorWindow()->rebuild();
}
{
copy(waveId, a, b);
m::wfx::cut(waveId, a, b);
+ updateWavePtr_(channelId, waveId);
resetBeginEnd_(channelId);
}
void copy(ID waveId, int a, int b)
{
m::model::WavesLock lock(m::model::waves);
-
waveBuffer_ = m::waveManager::createFromWave(m::model::get(m::model::waves, waveId), a, b);
}
}
m::wfx::paste(*waveBuffer_, waveId, a);
+ updateWavePtr_(channelId, waveId);
/* Shift begin/end points to keep the previous position. */
m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
{
- begin = static_cast<m::SampleChannel&>(c).begin;
- end = static_cast<m::SampleChannel&>(c).end;
+ begin = c.samplePlayer->state->begin.load();
+ end = c.samplePlayer->state->end.load();
});
if (a < begin && a < end)
/* -------------------------------------------------------------------------- */
-void silence(ID waveId, int a, int b)
+void silence(ID channelId, ID waveId, int a, int b)
{
m::wfx::silence(waveId, a, b);
+ updateWavePtr_(channelId, waveId);
}
/* -------------------------------------------------------------------------- */
-void fade(ID waveId, int a, int b, int type)
+void fade(ID channelId, ID waveId, int a, int b, m::wfx::Fade type)
{
m::wfx::fade(waveId, a, b, type);
+ updateWavePtr_(channelId, waveId);
}
/* -------------------------------------------------------------------------- */
-void smoothEdges(ID waveId, int a, int b)
+void smoothEdges(ID channelId, ID waveId, int a, int b)
{
m::wfx::smooth(waveId, a, b);
+ updateWavePtr_(channelId, waveId);
}
/* -------------------------------------------------------------------------- */
-void reverse(ID waveId, int a, int b)
+void reverse(ID channelId, ID waveId, int a, int b)
{
m::wfx::reverse(waveId, a, b);
+ updateWavePtr_(channelId, waveId);
}
/* -------------------------------------------------------------------------- */
-void normalizeHard(ID waveId, int a, int b)
+void normalize(ID channelId, ID waveId, int a, int b)
{
- m::wfx::normalizeHard(waveId, a, b);
+ m::wfx::normalize(waveId, a, b);
+ updateWavePtr_(channelId, waveId);
}
void trim(ID channelId, ID waveId, int a, int b)
{
m::wfx::trim(waveId, a, b);
+ updateWavePtr_(channelId, waveId);
resetBeginEnd_(channelId);
}
/* -------------------------------------------------------------------------- */
-void setPlayHead(ID channelId, Frame f)
-{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- static_cast<m::SampleChannel&>(c).trackerPreview = f;
- });
- getSampleEditorWindow()->refresh();
+/* 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 setPreview(ID channelId, PreviewMode mode)
+void setPreviewTracker(Frame f)
{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
+ namespace mm = m::model;
+
+ mm::onGet(mm::channels, m::mixer::PREVIEW_CHANNEL_ID, [&](m::Channel& c)
{
- static_cast<m::SampleChannel&>(c).previewMode = mode;
+ c.samplePlayer->state->tracker.store(f);
});
-}
+ previewTracker_ = f;
-/* -------------------------------------------------------------------------- */
+ getSampleEditorWindow()->refresh();
+}
-void rewindPreview(ID channelId)
+void cleanupPreview()
{
- setPlayHead(channelId, 0);
+ namespace mm = m::model;
+
+ mm::ChannelsLock cl(mm::channels);
+ mm::get(mm::channels, m::mixer::PREVIEW_CHANNEL_ID).samplePlayer->loadWave(nullptr);
}
/* -------------------------------------------------------------------------- */
-void toNewChannel(ID channelId, int a, int b)
+void toNewChannel(ID channelId, ID waveId, int a, int b)
{
ID columnId = G_MainWin->keyboard->getChannel(channelId)->getColumnId();
- ID waveId;
-
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- waveId = static_cast<m::SampleChannel&>(c).waveId;
- });
m::model::onGet(m::model::waves, waveId, [&](m::Wave& w)
{
return;
std::string wavePath;
- Frame waveSize;
- m::model::onGet(m::model::waves, waveId, [&](m::Wave& w)
+ m::model::onGet(m::model::waves, waveId, [&](const m::Wave& w)
{
wavePath = w.getPath();
- waveSize = w.getSize();
});
- if (channel::loadChannel(channelId, wavePath) != G_RES_OK)
+ if (channel::loadChannel(channelId, wavePath) != G_RES_OK) {
+ v::gdAlert("Unable to reload sample!");
return;
+ }
- ID newWaveId;
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
- {
- m::SampleChannel& sc = static_cast<m::SampleChannel&>(c);
- newWaveId = sc.waveId;
- });
-
- getSampleEditorWindow()->setWaveId(newWaveId);
getSampleEditorWindow()->rebuild();
}
void shift(ID channelId, ID waveId, int offset)
{
Frame shift;
-
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
+ m::model::onGet(m::model::channels, channelId, [&](const m::Channel& c)
{
- shift = static_cast<m::SampleChannel&>(c).shift;
+ shift = c.samplePlayer->state->shift.load();
});
m::wfx::shift(waveId, offset - shift);
+ updateWavePtr_(channelId, waveId);
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
+ m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
{
- static_cast<m::SampleChannel&>(c).shift = offset;
+ c.samplePlayer->state->shift.store(offset);
});
+
+ getSampleEditorWindow()->shiftTool->update(offset);
}
-}}}; // giada::c::sampleEditor::
+}}} // giada::c::sampleEditor::
#define G_GLUE_SAMPLE_EDITOR_H
+#include <functional>
+#include <string>
#include "core/types.h"
+#include "core/waveFx.h"
namespace giada {
+namespace m
+{
+class Channel;
+class Wave;
+}
+namespace v
+{
+class gdSampleEditor;
+}
namespace c {
namespace sampleEditor
{
+struct Data
+{
+ Data() = default;
+ Data(const m::Channel&, const m::Wave&);
+
+ ChannelStatus a_getPreviewStatus() const;
+ Frame a_getPreviewTracker() const;
+
+ ID channelId;
+ ID waveId;
+ std::string name;
+ float volume;
+ float pan;
+ float pitch;
+ Frame begin;
+ Frame end;
+ Frame shift;
+ Frame waveSize;
+ int waveBits;
+ int waveDuration;
+ int waveRate;
+ std::string wavePath;
+ bool isLogical;
+};
+
+/* onRefresh --- TODO - wrong name */
+
+void onRefresh(bool gui, std::function<void(v::gdSampleEditor&)> 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 paste(ID channelId, ID waveId, int a);
void trim(ID channelId, ID waveId, int a, int b);
-void reverse(ID waveId, int a, int b);
-void normalizeHard(ID waveId, int a, int b);
-void silence(ID waveId, int a, int b);
-void fade(ID waveId, int a, int b, int type);
-void smoothEdges(ID waveId, int a, int b);
+void reverse(ID channelId, ID waveId, int a, int b);
+void normalize(ID channelId, ID waveId, int a, int b);
+void silence(ID channelId, ID waveId, int a, int b);
+void fade(ID channelId, ID waveId, int a, int b, m::wfx::Fade type);
+void smoothEdges(ID channelId, ID waveId, int a, int b);
void shift(ID channelId, ID waveId, int offset);
void reload(ID channelId, ID waveId);
bool isWaveBufferFull();
-/* setPlayHead
-Changes playhead's position. Used in preview. */
-
-void setPlayHead(ID channelId, Frame f);
-
-void setPreview(ID channelId, PreviewMode mode);
-void rewindPreview(ID channelId);
+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, int a, int b);
-}}}; // giada::c::sampleEditor::
+void toNewChannel(ID channelId, ID waveId, int a, int b);
+}}} // giada::c::sampleEditor::
#endif
#include <cassert>
#include "core/model/model.h"
#include "core/model/storage.h"
-#include "core/channels/channel.h"
-#include "core/channels/sampleChannel.h"
-#include "core/channels/midiChannel.h"
#include "core/mixer.h"
#include "core/wave.h"
#include "core/mixerHandler.h"
#include "core/recorderHandler.h"
-#include "core/pluginManager.h"
-#include "core/pluginHost.h"
-#include "core/plugin.h"
+#include "core/plugins/pluginManager.h"
+#include "core/plugins/pluginHost.h"
+#include "core/plugins/plugin.h"
#include "core/conf.h"
#include "core/patch.h"
#include "core/init.h"
if (isWavePathUnique_(w, path))
return path;
+ // TODO - just use a timestamp. e.g. makeWavePath_(..., ..., getTimeStamp())
int k = 0;
path = makeWavePath_(base, w, k);
while (!isWavePathUnique_(w, path))
m::model::store(m::patch::patch);
v::model::store(m::patch::patch);
- if (!m::patch::write(name, path))
+ if (!m::patch::write(path))
return false;
u::gui::updateMainWinLabel(name);
m::conf::conf.patchPath = u::fs::getUpDir(u::fs::getUpDir(path));
- u::log::print("[savePatch] patch saved as %s\n", path.c_str());
+ u::log::print("[savePatch] patch saved as %s\n", path);
return true;
}
/* -------------------------------------------------------------------------- */
-void saveWavesToProject_(const std::string& base)
+void saveWavesToProject_(const std::string& basePath)
{
- for (size_t i = 0; i < m::model::waves.size(); i++) {
- m::model::onSwap(m::model::waves, m::model::getId(m::model::waves, i), [&](m::Wave& w)
- {
- w.setPath(makeUniqueWavePath_(base, w));
- m::waveManager::save(w, w.getPath()); // TODO - error checking
- });
+ /* No need for a hard Wave swap here: nobody is reading the path data. */
+
+ m::model::WavesLock l(m::model::waves);
+
+ for (m::Wave* w : m::model::waves) {
+ w->setPath(makeUniqueWavePath_(basePath, *w));
+ m::waveManager::save(*w, w->getPath()); // TODO - error checking
}
}
} // {anonymous}
void loadProject(void* data)
{
- v::gdBrowserLoad* browser = (v::gdBrowserLoad*) data;
+ v::gdBrowserLoad* browser = static_cast<v::gdBrowserLoad*>(data);
std::string fullPath = browser->getSelectedItem();
bool isProject = u::fs::isProject(browser->getSelectedItem());
browser->showStatusBar();
- u::log::print("[loadProject] load from %s\n", fullPath.c_str());
+ u::log::print("[loadProject] load from %s\n", fullPath);
std::string fileToLoad = fullPath; // patch file to read from
std::string basePath = ""; // base path, in case of reading from a project
the current samplerate != patch samplerate. Clock needs to update frames
in sequencer. */
- m::mixer::allocVirtualInput(m::clock::getFramesInLoop());
m::mh::updateSoloCount();
m::recorderHandler::updateSamplerate(m::conf::conf.samplerate, m::patch::patch.samplerate);
m::clock::recomputeFrames();
+ m::mixer::allocRecBuffer(m::clock::getFramesInLoop());
/* Mixer is ready to go back online. */
void saveProject(void* data)
{
- v::gdBrowserSave* browser = (v::gdBrowserSave*) data;
- std::string name = u::fs::stripExt(browser->getName());
- std::string folderPath = browser->getCurrentPath();
- std::string fullPath = folderPath + G_SLASH + name + ".gprj";
- std::string gptcPath = fullPath + G_SLASH + name + ".gptc";
+ v::gdBrowserSave* browser = static_cast<v::gdBrowserSave*>(data);
+ std::string name = u::fs::stripExt(browser->getName());
+ std::string folderPath = browser->getCurrentPath();
+ std::string fullPath = folderPath + G_SLASH + name + ".gprj";
+ std::string gptcPath = fullPath + G_SLASH + name + ".gptc";
if (name == "") {
v::gdAlert("Please choose a project name.");
return;
}
- if (u::fs::isProject(fullPath) && !v::gdConfirmWin("Warning", "Project exists: overwrite?"))
+ if (u::fs::dirExists(fullPath) && !v::gdConfirmWin("Warning", "Project exists: overwrite?"))
return;
- if (!u::fs::dirExists(fullPath) && !u::fs::mkdir(fullPath)) {
+ if (!u::fs::mkdir(fullPath)) {
u::log::print("[saveProject] Unable to make project directory!\n");
return;
}
- u::log::print("[saveProject] Project dir created: %s\n", fullPath.c_str());
+ u::log::print("[saveProject] Project dir created: %s\n", fullPath);
saveWavesToProject_(fullPath);
void loadSample(void* data)
{
- v::gdBrowserLoad* browser = (v::gdBrowserLoad*) data;
+ v::gdBrowserLoad* browser = static_cast<v::gdBrowserLoad*>(data);
std::string fullPath = browser->getSelectedItem();
if (fullPath.empty())
void saveSample(void* data)
{
- v::gdBrowserSave* browser = (v::gdBrowserSave*) data;
+ v::gdBrowserSave* browser = static_cast<v::gdBrowserSave*>(data);
std::string name = browser->getName();
std::string folderPath = browser->getCurrentPath();
+ ID channelId = browser->getChannelId();
if (name == "") {
v::gdAlert("Please choose a file name.");
return;
ID waveId;
- m::model::onGet(m::model::channels, browser->getChannelId(), [&](m::Channel& c)
+ m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
{
- waveId = static_cast<m::SampleChannel&>(c).waveId;
+ waveId = c.samplePlayer->getWaveId();
});
- size_t waveIndex = m::model::getIndex(m::model::waves, waveId);
+ std::size_t waveIndex = m::model::getIndex(m::model::waves, waveId);
std::unique_ptr<m::Wave> wave = m::model::waves.clone(waveIndex);
v::gdAlert("Unable to save this sample!");
return;
}
-
- u::log::print("[saveSample] sample saved to %s\n", filePath.c_str());
-
+
+ u::log::print("[saveSample] sample saved to %s\n", filePath);
+
/* Update last used path in conf, so that it can be reused next time. */
m::conf::conf.samplePath = u::fs::dirname(filePath);
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <FL/Fl.H>
-#include "../gui/elems/mainWindow/mainTransport.h"
-#include "../gui/dialogs/mainWindow.h"
-#include "../core/clock.h"
-#include "../core/conf.h"
-#include "../core/const.h"
-#include "../core/kernelAudio.h"
-#include "../core/kernelMidi.h"
-#include "../core/mixerHandler.h"
-#include "../core/mixer.h"
-#include "../core/recorder.h"
-#include "../core/recManager.h"
-#include "transport.h"
-
-
-extern gdMainWindow* G_MainWin;
-
-
-using namespace giada::m;
-
-
-namespace giada {
-namespace c {
-namespace transport
-{
-void startStopSeq(bool gui)
-{
- clock::isRunning() ? stopSeq(gui) : startSeq(gui);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void startSeq(bool gui)
-{
- switch (clock::getStatus()) {
- case ClockStatus::STOPPED:
- clock::setStatus(ClockStatus::RUNNING);
- break;
- case ClockStatus::WAITING:
- clock::setStatus(ClockStatus::RUNNING);
- m::recManager::stopActionRec();
- G_MainWin->mainTransport->setRecTriggerModeActive(true);
- G_MainWin->mainTransport->updateRecAction(0);
- G_MainWin->mainTransport->updateRecInput(0);
- break;
- default:
- break;
- }
-
-#if defined(__linux__) || defined(__FreeBSD__)
- kernelAudio::jackStart();
-#endif
-
- if (!gui) {
- Fl::lock();
- G_MainWin->mainTransport->updatePlay(1);
- Fl::unlock();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void stopSeq(bool gui)
-{
- mh::stopSequencer();
-
-#if defined(__linux__) || defined(__FreeBSD__)
- kernelAudio::jackStop();
-#endif
-
- /* What to do if we stop the sequencer and some action recs are active?
- Deactivate the button and delete any 'rec on' status. */
-
- if (recorder::isActive()) {
- recorder::disable();
- Fl::lock();
- G_MainWin->mainTransport->updateRecAction(0);
- Fl::unlock();
- }
-
- /* If input recs are active (who knows why) we must deactivate them. One
- might stop the sequencer while an input rec is running. */
-
- if (mixer::recording) {
- mh::stopInputRec();
- Fl::lock();
- G_MainWin->mainTransport->updateRecInput(0);
- Fl::unlock();
- }
-
- if (!gui) {
- Fl::lock();
- G_MainWin->mainTransport->updatePlay(0);
- Fl::unlock();
- }
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void rewindSeq(bool gui, bool notifyJack)
-{
- mh::rewindSequencer();
-
- /* FIXME - potential desync when Quantizer is enabled from this point on.
- Mixer would wait, while the following calls would be made regardless of its
- state. */
-
-#ifdef __linux__
- if (notifyJack)
- kernelAudio::jackSetPosition(0);
-#endif
-
- if (conf::midiSync == MIDI_SYNC_CLOCK_M)
- kernelMidi::send(MIDI_POSITION_PTR, 0, 0);
-}
-
-/* -------------------------------------------------------------------------- */
-
-
-void toggleMetronome(bool gui)
-{
- mixer::toggleMetronome();
- if (!gui) {
- Fl::lock();
- G_MainWin->mainTransport->updateMetronome(mixer::isMetronomeOn());
- Fl::unlock();
- }
-}
-
-}}} // giada::c::transport::
\ No newline at end of file
#endif
end();
+ std::string version = G_VERSION_STR;
+#ifdef G_DEBUG_MODE
+ version += " (debug build)";
+#endif
+
logo->image(new Fl_Pixmap(giada_logo_xpm));
text->align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_TOP);
text->copy_label(std::string(
- "Version " + std::string(G_VERSION_STR) + " (" BUILD_DATE ")\n\n"
+ "Version " + version + " (" BUILD_DATE ")\n\n"
"Developed by Monocasual Laboratories\n\n"
"Released under the terms of the GNU General\n"
"Public License (GPL v3)\n\n"
/* -------------------------------------------------------------------------- */
-void gdAbout::cb_close(Fl_Widget* w, void* p) { ((gdAbout*)p)->cb_close(); }
+void gdAbout::cb_close(Fl_Widget* /*w*/, void* p) { ((gdAbout*)p)->cb_close(); }
/* -------------------------------------------------------------------------- */
gdAbout();
- static void cb_close(Fl_Widget* w, void* p);
+ static void cb_close(Fl_Widget* /*w*/, void* p);
inline void cb_close();
private:
#include <FL/fl_draw.H>
#include "utils/gui.h"
#include "utils/string.h"
-#include "core/channels/channel.h"
-#include "core/model/model.h"
#include "core/conf.h"
+#include "core/action.h"
#include "core/const.h"
#include "core/clock.h"
+#include "core/midiEvent.h"
+#include "glue/channel.h"
#include "gui/elems/actionEditor/gridTool.h"
-#include "gui/elems/basics/scroll.h"
+#include "gui/elems/basics/scrollPack.h"
#include "gui/elems/basics/choice.h"
#include "baseActionEditor.h"
namespace v
{
gdBaseActionEditor::gdBaseActionEditor(ID channelId)
-: gdWindow (640, 284),
- channelId(channelId),
- ratio (G_DEFAULT_ZOOM_RATIO)
+: gdWindow (640, 284)
+, channelId(channelId)
+, ratio (G_DEFAULT_ZOOM_RATIO)
{
using namespace giada::m;
/* -------------------------------------------------------------------------- */
-void gdBaseActionEditor::cb_zoomIn(Fl_Widget* w, void* p) { ((gdBaseActionEditor*)p)->zoomIn(); }
-void gdBaseActionEditor::cb_zoomOut(Fl_Widget* w, void* p) { ((gdBaseActionEditor*)p)->zoomOut(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-std::vector<m::Action> gdBaseActionEditor::getActions() const
-{
- return m_actions;
-}
+void gdBaseActionEditor::cb_zoomIn(Fl_Widget* /*w*/, void* p) { ((gdBaseActionEditor*)p)->zoomIn(); }
+void gdBaseActionEditor::cb_zoomOut(Fl_Widget* /*w*/, void* p) { ((gdBaseActionEditor*)p)->zoomOut(); }
/* -------------------------------------------------------------------------- */
{
u::gui::setFavicon(this);
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- std::string l = "Action Editor";
- if (c.name != "") l += " - " + c.name;
- copy_label(l.c_str());
- });
+ std::string l = "Action Editor";
+ if (m_data.channelName != "") l += " - " + m_data.channelName;
+ copy_label(l.c_str());
set_non_modal();
size_range(640, 284);
#include "core/types.h"
+#include "glue/actionEditor.h"
#include "gui/dialogs/window.h"
-class geChoice;
class geButton;
-class geScroll;
namespace giada {
}
namespace v
{
+class geChoice;
class geGridTool;
-
-
+class geScrollPack;
class gdBaseActionEditor : public gdWindow
{
public:
Pixel frameToPixel(Frame f) const;
Frame pixelToFrame(Pixel p, bool snap=true) const;
int getActionType() const;
- std::vector<m::Action> getActions() const;
-
- geChoice* actionType;
- geGridTool* gridTool;
- geButton* zoomInBtn;
- geButton* zoomOutBtn;
- geScroll* viewport; // widget container
ID channelId;
+ geChoice* actionType;
+ geGridTool* gridTool;
+ geButton* zoomInBtn;
+ geButton* zoomOutBtn;
+ geScrollPack* viewport; // widget container
+
float ratio;
Pixel fullWidth; // Full widgets width, i.e. scaled-down full sequencer
Pixel loopWidth; // Loop width, i.e. scaled-down sequencer range
static constexpr float MIN_RATIO = 25.0f;
static constexpr float MAX_RATIO = 40000.0f;
- std::vector<m::Action> m_actions;
-
gdBaseActionEditor(ID channelId);
void zoomIn();
void zoomOut();
- static void cb_zoomIn(Fl_Widget* w, void* p);
- static void cb_zoomOut(Fl_Widget* w, void* p);
+ static void cb_zoomIn(Fl_Widget* /*w*/, void* p);
+ static void cb_zoomOut(Fl_Widget* /*w*/, void* p);
/* computeWidth
Computes total width, in pixel. */
void centerViewportOut();
void prepareWindow();
+
+ c::actionEditor::Data m_data;
};
}} // giada::v::
#include <string>
-#include "core/channels/midiChannel.h"
-#include "core/model/model.h"
#include "core/graphics.h"
#include "glue/actionEditor.h"
-#include "gui/elems/basics/scroll.h"
+#include "glue/channel.h"
+#include "gui/elems/basics/scrollPack.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/resizerBar.h"
#include "gui/elems/basics/box.h"
gdMidiActionEditor::gdMidiActionEditor(ID channelId)
: gdBaseActionEditor(channelId)
{
- computeWidth();
-
- Fl_Group* upperArea = new Fl_Group(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w()-16, G_GUI_UNIT);
-
- upperArea->begin();
+ end();
- gridTool = new geGridTool(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN);
+ computeWidth();
- geBox *b1 = new geBox(gridTool->x()+gridTool->w()+4, 8, 300, G_GUI_UNIT); // padding actionType - zoomButtons
- zoomInBtn = new geButton(w()-8-40-4, G_GUI_OUTER_MARGIN, G_GUI_UNIT, G_GUI_UNIT, "", zoomInOff_xpm, zoomInOn_xpm);
- zoomOutBtn = new geButton(w()-8-20, G_GUI_OUTER_MARGIN, G_GUI_UNIT, G_GUI_UNIT, "", zoomOutOff_xpm, zoomOutOn_xpm);
-
- upperArea->end();
+ gePack* upperArea = new gePack(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, Direction::HORIZONTAL);
+ gridTool = new geGridTool(0, 0);
+ geBox* b1 = new geBox (0, 0, w() - 150, G_GUI_UNIT); // padding actionType - zoomButtons
+ zoomInBtn = new geButton (0, 0, G_GUI_UNIT, G_GUI_UNIT, "", zoomInOff_xpm, zoomInOn_xpm);
+ zoomOutBtn = new geButton (0, 0, G_GUI_UNIT, G_GUI_UNIT, "", zoomOutOff_xpm, zoomOutOn_xpm);
+ upperArea->add(gridTool);
+ upperArea->add(b1);
+ upperArea->add(zoomInBtn);
+ upperArea->add(zoomOutBtn);
upperArea->resizable(b1);
- zoomInBtn->callback(cb_zoomIn, (void*)this);
- zoomOutBtn->callback(cb_zoomOut, (void*)this);
-
/* Main viewport: contains all widgets. */
- viewport = new geScroll(G_GUI_OUTER_MARGIN, upperArea->y()+upperArea->h()+G_GUI_OUTER_MARGIN, w()-16, h()-44);
-
- m_ne = new geNoteEditor(viewport->x(), viewport->y(), this);
- m_ner = new geResizerBar(m_ne->x(), m_ne->y()+m_ne->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H, geResizerBar::VERTICAL);
+ viewport = new geScrollPack(G_GUI_OUTER_MARGIN, upperArea->y() + upperArea->h() + G_GUI_OUTER_MARGIN,
+ upperArea->w(), h()-44, Fl_Scroll::BOTH, Direction::VERTICAL, /*gutter=*/0);
+ m_ne = new geNoteEditor (0, 0, this);
+ m_ner = new geResizerBar (0, 0, viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H, geResizerBar::VERTICAL);
+ m_ve = new geVelocityEditor(0, 0, this);
+ m_ver = new geResizerBar (0, 0, viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H, geResizerBar::VERTICAL);
viewport->add(m_ne);
viewport->add(m_ner);
-
- m_ve = new geVelocityEditor(viewport->x(), m_ne->y()+m_ne->h()+RESIZER_BAR_H);
- m_ver = new geResizerBar(m_ve->x(), m_ve->y()+m_ve->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H, geResizerBar::VERTICAL);
viewport->add(m_ve);
viewport->add(m_ver);
- end();
+ zoomInBtn->callback(cb_zoomIn, (void*)this);
+ zoomOutBtn->callback(cb_zoomOut, (void*)this);
+
+ add(upperArea);
+ add(viewport);
+ resizable(upperArea);
+
prepareWindow();
rebuild();
}
void gdMidiActionEditor::rebuild()
{
- m_actions = c::actionEditor::getActions(channelId);
+ m_data = c::actionEditor::getData(channelId);
computeWidth();
- m_ne->rebuild();
+ m_ne->rebuild(m_data);
m_ner->size(m_ne->w(), m_ner->h());
- m_ve->rebuild();
+ m_ve->rebuild(m_data);
m_ver->size(m_ve->w(), m_ver->h());
}
}} // giada::v::
#include <string>
#include "core/model/model.h"
-#include "core/channels/sampleChannel.h"
#include "core/const.h"
#include "core/midiEvent.h"
#include "core/graphics.h"
#include "glue/actionEditor.h"
-#include "gui/elems/basics/scroll.h"
+#include "glue/channel.h"
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/scrollPack.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/resizerBar.h"
#include "gui/elems/basics/choice.h"
gdSampleActionEditor::gdSampleActionEditor(ID channelId)
: gdBaseActionEditor(channelId)
{
+ end();
+
computeWidth();
/* Container with zoom buttons and the action type selector. Scheme of the
resizable boxes: |[--b1--][actionType][--b2--][+][-]| */
- Fl_Group* upperArea = new Fl_Group(8, 8, w()-16, 20);
-
- upperArea->begin();
-
- actionType = new geChoice(8, 8, 80, 20);
- gridTool = new geGridTool(actionType->x()+actionType->w()+8, 8);
- actionType->add("Key press");
- actionType->add("Key release");
- actionType->add("Kill chan");
- actionType->value(0);
-
- if (!canChangeActionType())
- actionType->deactivate();
-
- geBox* b1 = new geBox(gridTool->x()+gridTool->w()+4, 8, 300, 20); // padding actionType - zoomButtons
- zoomInBtn = new geButton(w()-8-40-4, 8, 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
- zoomOutBtn = new geButton(w()-8-20, 8, 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
-
- upperArea->end();
+ gePack* upperArea = new gePack(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, Direction::HORIZONTAL);
+ actionType = new geChoice (0, 0, 80, 20);
+ gridTool = new geGridTool(0, 0);
+ geBox* b1 = new geBox (0, 0, w() - 232, 20); // padding actionType - zoomButtons
+ zoomInBtn = new geButton (0, 0, 20, 20, "", zoomInOff_xpm, zoomInOn_xpm);
+ zoomOutBtn = new geButton (0, 0, 20, 20, "", zoomOutOff_xpm, zoomOutOn_xpm);
+ upperArea->add(actionType);
+ upperArea->add(gridTool);
+ upperArea->add(b1);
+ upperArea->add(zoomInBtn);
+ upperArea->add(zoomOutBtn);
upperArea->resizable(b1);
+ actionType->add("Key press");
+ actionType->add("Key release");
+ actionType->add("Kill chan");
+ actionType->value(0);
+ if (!canChangeActionType())
+ actionType->deactivate();
+
zoomInBtn->callback(cb_zoomIn, (void*)this);
zoomOutBtn->callback(cb_zoomOut, (void*)this);
/* Main viewport: contains all widgets. */
- viewport = new geScroll(8, 36, w()-16, h()-44);
-
- m_ae = new geSampleActionEditor(viewport->x(), viewport->y());
- m_aer = new geResizerBar(m_ae->x(), m_ae->y()+m_ae->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H, geResizerBar::VERTICAL);
+ viewport = new geScrollPack(G_GUI_OUTER_MARGIN, upperArea->y() + upperArea->h() + G_GUI_OUTER_MARGIN,
+ upperArea->w(), h()-44, Fl_Scroll::BOTH, Direction::VERTICAL, /*gutter=*/0);
+ m_ae = new geSampleActionEditor(0, 0, this);
+ m_aer = new geResizerBar (0, 0, viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H, geResizerBar::VERTICAL);
+ m_ee = new geEnvelopeEditor (0, 0, "volume", this);
+ m_eer = new geResizerBar (0, 0, viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H, geResizerBar::VERTICAL);
viewport->add(m_ae);
viewport->add(m_aer);
-
- m_ee = new geEnvelopeEditor(viewport->x(), m_ae->y()+m_ae->h()+RESIZER_BAR_H, "volume");
- m_eer = new geResizerBar(m_ee->x(), m_ee->y()+m_ee->h(), viewport->w(), RESIZER_BAR_H, MIN_WIDGET_H, geResizerBar::VERTICAL);
viewport->add(m_ee);
viewport->add(m_eer);
- end();
+ add(upperArea);
+ add(viewport);
+ resizable(upperArea);
+
prepareWindow();
rebuild();
}
bool gdSampleActionEditor::canChangeActionType()
{
- bool res;
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- const m::SampleChannel& sc = static_cast<const m::SampleChannel&>(c);
- res = sc.mode != ChannelMode::SINGLE_PRESS && !sc.isAnyLoopMode();
- });
- return res;
+ return m_data.sample->channelMode != SamplePlayerMode::SINGLE_PRESS &&
+ m_data.sample->isLoopMode == false;
}
void gdSampleActionEditor::rebuild()
{
- m_actions = c::actionEditor::getActions(channelId);
+ m_data = c::actionEditor::getData(channelId);
canChangeActionType() ? actionType->activate() : actionType->deactivate();
computeWidth();
- m_ae->rebuild();
+ m_ae->rebuild(m_data);
m_aer->size(m_ae->w(), m_aer->h());
- m_ee->rebuild();
+ m_ee->rebuild(m_data);
m_eer->size(m_ee->w(), m_eer->h());
}
}} // giada::v::
{
class geSampleActionEditor;
class geEnvelopeEditor;
-
class gdSampleActionEditor : public gdBaseActionEditor
{
public:
private:
+ bool canChangeActionType();
+
geSampleActionEditor* m_ae;
geResizerBar* m_aer;
geEnvelopeEditor* m_ee;
geResizerBar* m_eer;
-
- bool canChangeActionType();
};
}} // giada::v::
/* -------------------------------------------------------------------------- */
-void gdBeatsInput::cb_update(Fl_Widget* w, void* p) { ((gdBeatsInput*)p)->cb_update(); }
+void gdBeatsInput::cb_update(Fl_Widget* /*w*/, void* p) { ((gdBeatsInput*)p)->cb_update(); }
/* -------------------------------------------------------------------------- */
private:
- static void cb_update(Fl_Widget* w, void* p);
+ static void cb_update(Fl_Widget* /*w*/, void* p);
void cb_update();
geInput* beats;
/* -------------------------------------------------------------------------- */
-void gdBpmInput::cb_update(Fl_Widget* w, void* p) { ((gdBpmInput*)p)->cb_update(); }
+void gdBpmInput::cb_update(Fl_Widget* /*w*/, void* p) { ((gdBpmInput*)p)->cb_update(); }
/* -------------------------------------------------------------------------- */
private:
- static void cb_update(Fl_Widget* w, void* p);
+ static void cb_update(Fl_Widget* /*w*/, void* p);
void cb_update();
geInput* input_a;
/* -------------------------------------------------------------------------- */
-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(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(); }
/* -------------------------------------------------------------------------- */
gdBrowserBase(const std::string& title, const std::string& path,
std::function<void(void*)> f, ID channelId);
- static void cb_up(Fl_Widget* v, void* p);
- static void cb_close(Fl_Widget* w, void* p);
- static void cb_toggleHiddenFiles(Fl_Widget* w, void* p);
+ static void cb_up(Fl_Widget* /*w*/, void* p);
+ static void cb_close(Fl_Widget* /*w*/, void* p);
+ static void cb_toggleHiddenFiles(Fl_Widget* /*w*/, void* p);
void cb_up();
void cb_close();
void cb_toggleHiddenFiles();
/* -------------------------------------------------------------------------- */
-void 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(Fl_Widget* /*v*/, void* p) { ((gdBrowserDir*)p)->cb_load(); }
+void gdBrowserDir::cb_down(Fl_Widget* /*v*/, void* p) { ((gdBrowserDir*)p)->cb_down(); }
/* -------------------------------------------------------------------------- */
private:
- static void cb_load(Fl_Widget* w, void* p);
- static void cb_down(Fl_Widget* w, void* p);
+ static void cb_load(Fl_Widget* /*w*/, void* p);
+ static void cb_down(Fl_Widget* /*w*/, void* p);
void cb_load();
void cb_down();
};
/* -------------------------------------------------------------------------- */
-void 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(Fl_Widget* /*v*/, void* p) { ((gdBrowserLoad*)p)->cb_load(); }
+void gdBrowserLoad::cb_down(Fl_Widget* /*v*/, void* p) { ((gdBrowserLoad*)p)->cb_down(); }
/* -------------------------------------------------------------------------- */
private:
- static void cb_load(Fl_Widget* w, void* p);
- static void cb_down(Fl_Widget* v, void* p);
+ static void cb_load(Fl_Widget* /*w*/, void* p);
+ static void cb_down(Fl_Widget* /*w*/, void* p);
void cb_load();
void cb_down();
};
/* -------------------------------------------------------------------------- */
-void 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_save(Fl_Widget* /*v*/, void* p) { ((gdBrowserSave*)p)->cb_save(); }
+void gdBrowserSave::cb_down(Fl_Widget* /*v*/, void* p) { ((gdBrowserSave*)p)->cb_down(); }
/* -------------------------------------------------------------------------- */
geInput* name;
- static void cb_down(Fl_Widget* v, void* p);
- static void cb_save(Fl_Widget* w, void* p);
+ static void cb_down(Fl_Widget* /*w*/, void* p);
+ static void cb_save(Fl_Widget* /*w*/, void* p);
void cb_down();
void cb_save();
};
#include "glue/channel.h"
#include "utils/gui.h"
-#include "core/channels/channel.h"
#include "core/model/model.h"
#include "core/const.h"
#include "core/conf.h"
namespace giada {
namespace v
{
-gdChannelNameInput::gdChannelNameInput(ID channelId)
-: gdWindow (u::gui::centerWindowX(400), u::gui::centerWindowY(64), 400, 64, "New channel name"),
- m_channelId(channelId)
+gdChannelNameInput::gdChannelNameInput(const c::channel::Data& d)
+: gdWindow(u::gui::centerWindowX(400), u::gui::centerWindowY(64), 400, 64, "New channel name"),
+ m_data (d)
{
set_modal();
m_cancel = new geButton(m_ok->x() - 70 - G_GUI_OUTER_MARGIN, m_ok->y(), 70, G_GUI_UNIT, "Cancel");
end();
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- m_name->value(c.name.c_str());
- });
+ m_name->value(m_data.name.c_str());
m_ok->shortcut(FL_Enter);
m_ok->callback(cb_update, (void*)this);
/* -------------------------------------------------------------------------- */
-void gdChannelNameInput::cb_update(Fl_Widget* w, void* p) { ((gdChannelNameInput*)p)->cb_update(); }
-void gdChannelNameInput::cb_cancel(Fl_Widget* w, void* p) { ((gdChannelNameInput*)p)->cb_cancel(); }
+void gdChannelNameInput::cb_update(Fl_Widget* /*w*/, void* p) { ((gdChannelNameInput*)p)->cb_update(); }
+void gdChannelNameInput::cb_cancel(Fl_Widget* /*w*/, void* p) { ((gdChannelNameInput*)p)->cb_cancel(); }
/* -------------------------------------------------------------------------- */
void gdChannelNameInput::cb_update()
{
- c::channel::setName(m_channelId, m_name->value());
+ c::channel::setName(m_data.id, m_name->value());
do_callback();
}
{
public:
- gdChannelNameInput(ID channelId);
+ gdChannelNameInput(const c::channel::Data& d);
private:
- static void cb_update(Fl_Widget* w, void* p);
- static void cb_cancel(Fl_Widget* w, void* p);
+ static void cb_update(Fl_Widget* /*w*/, void* p);
+ static void cb_cancel(Fl_Widget* /*w*/, void* p);
void cb_update();
void cb_cancel();
- ID m_channelId;
+ const c::channel::Data& m_data;
geInput* m_name;
geButton* m_ok;
/* -------------------------------------------------------------------------- */
-void gdConfig::cb_save_config(Fl_Widget* w, void* p) { ((gdConfig*)p)->cb_save_config(); }
-void gdConfig::cb_cancel (Fl_Widget* w, void* p) { ((gdConfig*)p)->cb_cancel(); }
+void gdConfig::cb_save_config(Fl_Widget* /*w*/, void* p) { ((gdConfig*)p)->cb_save_config(); }
+void gdConfig::cb_cancel (Fl_Widget* /*w*/, void* p) { ((gdConfig*)p)->cb_cancel(); }
/* -------------------------------------------------------------------------- */
class geTabPlugins;
#endif
class geButton;
-class geChoice;
class geCheck;
class geInput;
class geRadio;
namespace giada {
namespace v
{
+class geChoice;
class gdConfig : public gdWindow
{
public:
private:
- static void cb_save_config(Fl_Widget* w, void* p);
- static void cb_cancel(Fl_Widget* w, void* p);
+ static void cb_save_config(Fl_Widget* /*w*/, void* p);
+ static void cb_cancel(Fl_Widget* /*w*/, void* p);
void cb_save_config();
void cb_cancel();
};
#include "utils/string.h"
#include "glue/io.h"
#include "core/model/model.h"
-#include "core/channels/channel.h"
-#include "core/channels/sampleChannel.h"
-#include "core/channels/midiChannel.h"
#include "core/conf.h"
#include "utils/log.h"
#include "gui/elems/basics/box.h"
namespace giada {
namespace v
{
-gdKeyGrabber::gdKeyGrabber(ID channelId)
-: gdWindow (300, 126, "Key configuration"),
- m_channelId(channelId)
+gdKeyGrabber::gdKeyGrabber(const c::channel::Data& d)
+: gdWindow(300, 126, "Key configuration")
+, m_data (d)
{
begin();
m_text = new geBox(8, 8, 284, 80, "");
m_clear->callback(cb_clear, (void*)this);
m_cancel->callback(cb_cancel, (void*)this);
- rebuild();
+ updateText(m_data.key);
u::gui::setFavicon(this);
set_modal();
void gdKeyGrabber::rebuild()
{
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- updateText(c.key);
- });
+ updateText(m_data.key);
}
/* -------------------------------------------------------------------------- */
-void gdKeyGrabber::cb_clear (Fl_Widget* w, void* p) { ((gdKeyGrabber*)p)->cb_clear(); }
-void gdKeyGrabber::cb_cancel(Fl_Widget* w, void* p) { ((gdKeyGrabber*)p)->cb_cancel(); }
+void gdKeyGrabber::cb_clear (Fl_Widget* /*w*/, void* p) { ((gdKeyGrabber*)p)->cb_clear(); }
+void gdKeyGrabber::cb_cancel(Fl_Widget* /*w*/, void* p) { ((gdKeyGrabber*)p)->cb_cancel(); }
/* -------------------------------------------------------------------------- */
void gdKeyGrabber::setButtonLabel(int key)
{
- c::io::setSampleChannelKey(m_channelId, key);
+ c::io::channel_setKey(m_data.id, key);
}
/* -------------------------------------------------------------------------- */
{
std::string tmp = "Press a key.\n\nCurrent binding: ";
if (key != 0)
- tmp += static_cast<char>(key);
+ tmp += static_cast<wchar_t>(key);
else
tmp += "[none]";
m_text->copy_label(tmp.c_str());
&& x != FL_End
&& x != ' ')
{
- u::log::print("set key '%c' (%d) for channel ID=%d\n", x, x, m_channelId);
+ u::log::print("set key '%c' (%d) for channel ID=%d\n", x, x, m_data.id);
setButtonLabel(x);
updateText(x);
break;
namespace giada {
+namespace c {
+namespace channel
+{
+struct Data;
+}}
namespace v
{
class gdKeyGrabber : public gdWindow
{
public:
- gdKeyGrabber(ID channelId);
+ gdKeyGrabber(const c::channel::Data& d);
int handle(int e) override;
void rebuild() override;
private:
- static void cb_clear (Fl_Widget* w, void* p);
- static void cb_cancel(Fl_Widget* w, void* p);
+ static void cb_clear (Fl_Widget* /*w*/, void* p);
+ static void cb_cancel(Fl_Widget* /*w*/, void* p);
void cb_clear ();
void cb_cancel();
void setButtonLabel(int key);
void updateText(int key);
- ID m_channelId;
+ const c::channel::Data& m_data;
geBox* m_text;
geButton* m_clear;
mainMenu = new v::geMainMenu(8, 0);
#if defined(WITH_VST)
- mainIO = new v::geMainIO(408, 8);
+ mainIO = new v::geMainIO(412, 8);
#else
- mainIO = new v::geMainIO(476, 8);
+ mainIO = new v::geMainIO(460, 8);
#endif
mainTransport = new v::geMainTransport(8, 39);
- mainTimer = new v::geMainTimer(598, 44);
+ mainTimer = new v::geMainTimer(571, 44);
beatMeter = new v::geBeatMeter(100, 83, 609, 20);
keyboard = new v::geKeyboard(8, 122, w()-16, 380);
add(zone2);
add(zone3);
add(keyboard);
- callback([](Fl_Widget* w, void* v) {
+ callback([](Fl_Widget* /*w*/, void* /*v*/) {
m::init::closeMainWindow();
});
u::gui::setFavicon(this);
/* -------------------------------------------------------------------------- */
-void gdMidiInputBase::refresh()
-{
- for (geMidiLearnerBase* l : m_learners)
- l->refresh();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdMidiInputBase::cb_close(Fl_Widget* w, void* p) { ((gdMidiInputBase*)p)->cb_close(); }
+void gdMidiInputBase::cb_close(Fl_Widget* /*w*/, void* p) { ((gdMidiInputBase*)p)->cb_close(); }
/* -------------------------------------------------------------------------- */
#include "gui/dialogs/window.h"
-#include "gui/elems/midiIO/midiLearnerBase.h"
+#include "gui/elems/midiIO/midiLearner.h"
class geButton;
class geCheck;
-class geChoice;
namespace giada {
namespace v
{
+class geChoice;
class gdMidiInputBase : public gdWindow
{
public:
virtual ~gdMidiInputBase();
- void refresh() override;
-
protected:
- gdMidiInputBase(int x, int y, int w, int h, const char* title);
+ gdMidiInputBase(int x, int y, int w, int h, const char* title="");
- static const int LEARNER_WIDTH = 284;
-
- static void cb_close(Fl_Widget* w, void* p);
+ static void cb_close(Fl_Widget* /*w*/, void* p);
void cb_close();
- std::vector<geMidiLearnerBase*> m_learners;
-
geButton* m_ok;
geCheck* m_enable;
geChoice* m_channel;
#include <FL/Fl_Pack.H>
#include "utils/gui.h"
#include "utils/log.h"
-#include "core/model/model.h"
-#include "core/channels/sampleChannel.h"
-#include "core/model/model.h"
#include "core/const.h"
#include "core/conf.h"
#ifdef WITH_VST
-#include "core/plugin.h"
+#include "core/plugins/plugin.h"
#endif
#include "utils/string.h"
-#include "gui/elems/midiIO/midiLearnerChannel.h"
-#include "gui/elems/midiIO/midiLearnerPlugin.h"
-#include "gui/elems/basics/scroll.h"
+#include "gui/elems/midiIO/midiLearner.h"
+#include "gui/elems/midiIO/midiLearnerPack.h"
+#include "gui/elems/basics/scrollPack.h"
#include "gui/elems/basics/box.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/choice.h"
#include "gui/elems/basics/check.h"
+#include "gui/elems/basics/group.h"
#include "midiInputChannel.h"
namespace giada {
namespace v
{
-gdMidiInputChannel::gdMidiInputChannel(ID channelId)
-: gdMidiInputBase(m::conf::conf.midiInputX, m::conf::conf.midiInputY, m::conf::conf.midiInputW,
- m::conf::conf.midiInputH, "MIDI Input Setup"),
- m_channelId (channelId)
+geChannelLearnerPack::geChannelLearnerPack(int x, int y, const c::io::Channel_InputData& channel)
+: geMidiLearnerPack(x, y, "Channel")
{
- m::model::ChannelsLock l(m::model::channels);
- const m::Channel& c = m::model::get(m::model::channels, m_channelId);
+ setCallbacks(
+ [channelId=channel.channelId] (int param) { c::io::channel_startMidiLearn(param, channelId); },
+ [channelId=channel.channelId] (int param) { c::io::channel_clearMidiLearn(param, channelId); }
+ );
+ addMidiLearner("keyPress", G_MIDI_IN_KEYPRESS);
+ addMidiLearner("key release", G_MIDI_IN_KEYREL);
+ addMidiLearner("key kill", G_MIDI_IN_KILL);
+ addMidiLearner("arm", G_MIDI_IN_ARM);
+ addMidiLearner("mute", G_MIDI_IN_MUTE);
+ addMidiLearner("solo", G_MIDI_IN_SOLO);
+ addMidiLearner("volume", G_MIDI_IN_VOLUME);
+ addMidiLearner("pitch", G_MIDI_IN_PITCH, /*visible=*/channel.channelType == ChannelType::SAMPLE);
+ addMidiLearner("read actions", G_MIDI_IN_READ_ACTIONS, /*visible=*/channel.channelType == ChannelType::SAMPLE);
+}
- copy_label(std::string("MIDI Input Setup (channel " + std::to_string(c.id) + ")").c_str());
-
- int extra = c.type == ChannelType::SAMPLE ? 28 : 0;
- Fl_Group* groupHeader = new Fl_Group(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w(), 20 + extra);
- groupHeader->begin();
+/* -------------------------------------------------------------------------- */
- m_enable = new geCheck(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT,
- "Enable MIDI input");
- m_channel = new geChoice(m_enable->x()+m_enable->w()+44, G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT);
- m_veloAsVol = new geCheck(G_GUI_OUTER_MARGIN, m_enable->y()+m_enable->h()+G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT,
- "Velocity drives volume (one-shot only)");
- groupHeader->resizable(nullptr);
- groupHeader->end();
+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);
+}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
- m_container = new geScroll(G_GUI_OUTER_MARGIN, groupHeader->y()+groupHeader->h()+G_GUI_OUTER_MARGIN,
- w()-16, h()-72-extra);
- m_container->begin();
- addChannelLearners();
#ifdef WITH_VST
- addPluginLearners();
+
+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
- m_container->end();
- for (auto* l : m_learners)
- c.midiIn ? l->activate() : l->deactivate();
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
+gdMidiInputChannel::gdMidiInputChannel(ID channelId)
+: gdMidiInputBase(m::conf::conf.midiInputX,
+ m::conf::conf.midiInputY,
+ m::conf::conf.midiInputW,
+ m::conf::conf.midiInputH)
+, m_channelId (channelId)
+, m_data (c::io::channel_getInputData(channelId))
+{
+ end();
+
+ copy_label(std::string("MIDI Input Setup (channel " + std::to_string(channelId) + ")").c_str());
+
+ /* Header */
+
+ geGroup* groupHeader = new geGroup(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN);
+ m_enable = new geCheck(0, 0, 120, G_GUI_UNIT, "Enable MIDI input");
+ m_channel = new geChoice(m_enable->x() + m_enable->w() + 44, 0, 120, G_GUI_UNIT);
+ m_veloAsVol = new geCheck(0, m_enable->y() + m_enable->h() + G_GUI_OUTER_MARGIN, w() - 16, G_GUI_UNIT,
+ "Velocity drives volume (Sample Channels)");
+ groupHeader->add(m_enable);
+ groupHeader->add(m_channel);
+ groupHeader->add(m_veloAsVol);
+ groupHeader->resizable(nullptr);
+
+ /* Main scrollable content. */
- Fl_Group* groupButtons = new Fl_Group(8, m_container->y()+m_container->h()+8, m_container->w(), 20);
- groupButtons->begin();
+ 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
- geBox* spacer = new geBox(groupButtons->x(), groupButtons->y(), 100, 20); // spacer window border <-> buttons
- m_ok = new geButton(w()-88, groupButtons->y(), 80, 20, "Close");
+ /* Footer buttons. */
+ geGroup* groupButtons = new geGroup(G_GUI_OUTER_MARGIN, m_container->y() + m_container->h() + G_GUI_OUTER_MARGIN);
+ geBox* spacer = new geBox(0, 0, w() - 80, G_GUI_UNIT); // spacer window border <-> buttons
+ m_ok = new geButton(w() - 96, 0, 80, G_GUI_UNIT, "Close");
+ groupButtons->add(spacer);
+ groupButtons->add(m_ok);
groupButtons->resizable(spacer);
- groupButtons->end();
m_ok->callback(cb_close, (void*)this);
-
- m_enable->value(c.midiIn);
m_enable->callback(cb_enable, (void*)this);
- if (c.type == ChannelType::SAMPLE) {
- m_veloAsVol->value(static_cast<const m::SampleChannel&>(c).midiInVeloAsVol);
- m_veloAsVol->callback(cb_veloAsVol, (void*)this);
- }
- else
- m_veloAsVol->hide();
-
m_channel->add("Channel (any)");
m_channel->add("Channel 1");
m_channel->add("Channel 2");
m_channel->add("Channel 14");
m_channel->add("Channel 15");
m_channel->add("Channel 16");
- m_channel->value(c.midiInFilter == -1 ? 0 : c.midiInFilter + 1);
m_channel->callback(cb_setChannel, (void*)this);
- resizable(m_container);
+ m_veloAsVol->callback(cb_veloAsVol, (void*)this);
- end();
+ add(groupHeader);
+ add(m_container);
+ add(groupButtons);
+ resizable(m_container);
u::gui::setFavicon(this);
set_modal();
+ rebuild();
show();
}
/* -------------------------------------------------------------------------- */
-void gdMidiInputChannel::addChannelLearners()
+void gdMidiInputChannel::rebuild()
{
- m::model::ChannelsLock l(m::model::channels);
- const m::Channel& c = m::model::get(m::model::channels, m_channelId);
-
- Fl_Pack* pack = new Fl_Pack(m_container->x(), m_container->y(), LEARNER_WIDTH, 200);
- pack->spacing(G_GUI_INNER_MARGIN);
- pack->begin();
- geBox* header = new geBox(0, 0, LEARNER_WIDTH, G_GUI_UNIT, "Channel");
- header->box(FL_BORDER_BOX);
- m_learners.push_back(new geMidiLearnerChannel(0, 0, LEARNER_WIDTH, "key press", G_MIDI_IN_KEYPRESS, c.midiInKeyPress, m_channelId));
- m_learners.push_back(new geMidiLearnerChannel(0, 0, LEARNER_WIDTH, "key release", G_MIDI_IN_KEYREL, c.midiInKeyRel, m_channelId));
- m_learners.push_back(new geMidiLearnerChannel(0, 0, LEARNER_WIDTH, "key kill", G_MIDI_IN_KILL, c.midiInKill, m_channelId));
- m_learners.push_back(new geMidiLearnerChannel(0, 0, LEARNER_WIDTH, "arm", G_MIDI_IN_ARM, c.midiInArm, m_channelId));
- m_learners.push_back(new geMidiLearnerChannel(0, 0, LEARNER_WIDTH, "mute", G_MIDI_IN_MUTE, c.midiInVolume, m_channelId));
- m_learners.push_back(new geMidiLearnerChannel(0, 0, LEARNER_WIDTH, "solo", G_MIDI_IN_SOLO, c.midiInMute, m_channelId));
- m_learners.push_back(new geMidiLearnerChannel(0, 0, LEARNER_WIDTH, "volume", G_MIDI_IN_VOLUME, c.midiInSolo, m_channelId));
- if (c.type == ChannelType::SAMPLE) {
- const m::SampleChannel& sc = static_cast<const m::SampleChannel&>(c);
- m_learners.push_back(new geMidiLearnerChannel(0, 0, LEARNER_WIDTH, "pitch", G_MIDI_IN_PITCH, sc.midiInPitch, m_channelId));
- m_learners.push_back(new geMidiLearnerChannel(0, 0, LEARNER_WIDTH, "read actions", G_MIDI_IN_READ_ACTIONS, sc.midiInReadActions, m_channelId));
- }
- pack->end();
-}
+ 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<geChannelLearnerPack*>(m_container->getChild(i++))->update(m_data);
#ifdef WITH_VST
+ for (c::io::PluginData& plugin : m_data.plugins)
+ static_cast<gePluginLearnerPack*>(m_container->getChild(i++))->update(plugin, m_data.enabled);
+#endif
-void gdMidiInputChannel::addPluginLearners()
-{
- m::model::ChannelsLock cl(m::model::channels);
- m::model::PluginsLock ml(m::model::plugins);
-
- m::Channel& c = m::model::get(m::model::channels, m_channelId);
-
- int i = 1;
- for (ID id : c.pluginIds) {
-
- m::Plugin& p = m::model::get(m::model::plugins, id);
-
- Fl_Pack* pack = new Fl_Pack(m_container->x() + (i++ * (LEARNER_WIDTH + G_GUI_OUTER_MARGIN)),
- m_container->y(), LEARNER_WIDTH, 200);
- pack->spacing(G_GUI_INNER_MARGIN);
- pack->begin();
-
- geBox* header = new geBox(0, 0, LEARNER_WIDTH, G_GUI_UNIT, p.getName().c_str());
- header->box(FL_BORDER_BOX);
-
- for (int k = 0; k < p.getNumParameters(); k++)
- m_learners.push_back(new geMidiLearnerPlugin(0, 0, LEARNER_WIDTH, p.getParameterName(k), k, p.midiInParams.at(k), p.id));
+ m_channel->value(m_data.filter == -1 ? 0 : m_data.filter + 1);
- pack->end();
+ if (m_data.enabled) {
+ m_channel->activate();
+ if (m_data.channelType == ChannelType::SAMPLE)
+ m_veloAsVol->activate();
+ }
+ else {
+ m_channel->deactivate();
+ m_veloAsVol->deactivate();
}
}
-#endif
-
/* -------------------------------------------------------------------------- */
-void gdMidiInputChannel::cb_enable(Fl_Widget* w, void* p) { ((gdMidiInputChannel*)p)->cb_enable(); }
-void gdMidiInputChannel::cb_setChannel(Fl_Widget* w, void* p) { ((gdMidiInputChannel*)p)->cb_setChannel(); }
-void gdMidiInputChannel::cb_veloAsVol(Fl_Widget* w, void* p) { ((gdMidiInputChannel*)p)->cb_veloAsVol(); }
+void gdMidiInputChannel::cb_enable(Fl_Widget* /*w*/, void* p) { ((gdMidiInputChannel*)p)->cb_enable(); }
+void gdMidiInputChannel::cb_setChannel(Fl_Widget* /*w*/, void* p) { ((gdMidiInputChannel*)p)->cb_setChannel(); }
+void gdMidiInputChannel::cb_veloAsVol(Fl_Widget* /*w*/, void* p) { ((gdMidiInputChannel*)p)->cb_veloAsVol(); }
/* -------------------------------------------------------------------------- */
void gdMidiInputChannel::cb_enable()
{
- m::model::onSwap(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- c.midiIn = m_enable->value();
- });
-
- m_enable->value() ? m_channel->activate() : m_channel->deactivate();
-
- for (auto* l : m_learners)
- m_enable->value() ? l->activate() : l->deactivate();
+ c::io::channel_enableMidiLearn(m_data.channelId, m_enable->value());
}
void gdMidiInputChannel::cb_veloAsVol()
{
- m::model::onSwap(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- static_cast<m::SampleChannel&>(c).midiInVeloAsVol = m_veloAsVol->value();
- });
+ c::io::channel_enableVelocityAsVol(m_data.channelId, m_veloAsVol->value());
}
void gdMidiInputChannel::cb_setChannel()
{
- m::model::onSwap(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- c.midiInFilter = m_channel->value() == 0 ? -1 : m_channel->value() - 1;
- u::log::print("[gdMidiInputChannel] Set MIDI channel to %d\n", c.midiInFilter);
- });
+ c::io::channel_setMidiInputFilter(m_data.channelId,
+ m_channel->value() == 0 ? -1 : m_channel->value() - 1);
}
}} // giada::v::
#define GD_MIDI_INPUT_CHANNEL_H
+#include "glue/io.h"
+#include "gui/elems/midiIO/midiLearnerPack.h"
#include "midiInputBase.h"
-class geScroll;
class geCheck;
-class geChoice;
namespace giada {
namespace v
{
-class geMidiLearner;
-class gdMidiInputChannel : public gdMidiInputBase
+class geChoice;
+class geScrollPack;
+class geChannelLearnerPack : public geMidiLearnerPack
{
public:
- gdMidiInputChannel(ID channelId);
+ geChannelLearnerPack(int x, int y, const c::io::Channel_InputData& d);
-private:
+ void update(const c::io::Channel_InputData&);
+};
- static void cb_enable(Fl_Widget* w, void* p);
- static void cb_setChannel(Fl_Widget* w, void* p);
- static void cb_veloAsVol(Fl_Widget* w, void* p);
- void cb_enable();
- void cb_setChannel();
- void cb_veloAsVol();
- void addChannelLearners();
+/* -------------------------------------------------------------------------- */
+
#ifdef WITH_VST
- void addPluginLearners();
+class gePluginLearnerPack : public geMidiLearnerPack
+{
+public:
+
+ gePluginLearnerPack(int x, int y, const c::io::PluginData&);
+
+ void update(const c::io::PluginData&, bool enabled);
+};
#endif
+
+/* -------------------------------------------------------------------------- */
+
+
+
+class gdMidiInputChannel : public gdMidiInputBase
+{
+public:
+
+ gdMidiInputChannel(ID channelId);
+
+ void rebuild() override;
+
+private:
+
+ static void cb_enable(Fl_Widget* /*w*/, void* p);
+ static void cb_setChannel(Fl_Widget* /*w*/, void* p);
+ static void cb_veloAsVol(Fl_Widget* /*w*/, void* p);
+ void cb_enable();
+ void cb_setChannel();
+ void cb_veloAsVol();
+
ID m_channelId;
+
+ c::io::Channel_InputData m_data;
- geScroll* m_container;
- geCheck* m_veloAsVol;
+ geScrollPack* m_container;
+ geCheck* m_veloAsVol;
};
}} // giada::v::
#include "utils/gui.h"
#include "core/conf.h"
#include "core/const.h"
-#include "core/model/model.h"
-#include "gui/elems/midiIO/midiLearnerMaster.h"
+#include "gui/elems/midiIO/midiLearner.h"
+#include "gui/elems/basics/scrollPack.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/check.h"
#include "gui/elems/basics/choice.h"
+#include "gui/elems/basics/group.h"
#include "midiInputMaster.h"
namespace giada {
namespace v
{
-gdMidiInputMaster::gdMidiInputMaster()
-: gdMidiInputBase(m::conf::conf.midiInputX, m::conf::conf.midiInputY, 300, 284, "MIDI Input Setup (global)")
+geMasterLearnerPack::geMasterLearnerPack(int x, int y)
+: geMidiLearnerPack(x, y)
{
- set_modal();
+ setCallbacks(
+ [] (int param) { c::io::master_startMidiLearn(param); },
+ [] (int param) { c::io::master_clearMidiLearn(param); }
+ );
+ addMidiLearner("rewind", G_MIDI_IN_REWIND);
+ addMidiLearner("play/stop", G_MIDI_IN_START_STOP);
+ addMidiLearner("action recording", G_MIDI_IN_ACTION_REC);
+ addMidiLearner("input recording", G_MIDI_IN_INPUT_REC);
+ addMidiLearner("metronome", G_MIDI_IN_METRONOME);
+ addMidiLearner("input volume", G_MIDI_IN_VOLUME_IN);
+ addMidiLearner("output volume", G_MIDI_IN_VOLUME_OUT);
+ addMidiLearner("sequencer ×2", G_MIDI_IN_BEAT_DOUBLE);
+ addMidiLearner("sequencer ÷2", G_MIDI_IN_BEAT_HALF);
+}
- m::model::midiIn.lock();
- const m::model::MidiIn* midiIn = m::model::midiIn.get();
- Fl_Group* groupHeader = new Fl_Group(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w(), 20);
- groupHeader->begin();
+/* -------------------------------------------------------------------------- */
- m_enable = new geCheck(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT,
- "Enable MIDI input");
- m_channel = new geChoice(m_enable->x()+m_enable->w()+44, G_GUI_OUTER_MARGIN, 120, G_GUI_UNIT);
- groupHeader->resizable(nullptr);
- groupHeader->end();
-
- Fl_Pack* pack = new Fl_Pack(G_GUI_OUTER_MARGIN, groupHeader->y()+groupHeader->h()+G_GUI_OUTER_MARGIN,
- LEARNER_WIDTH, 212);
- pack->spacing(G_GUI_INNER_MARGIN);
- pack->begin();
- m_learners.push_back(new geMidiLearnerMaster(0, 0, LEARNER_WIDTH, "rewind", G_MIDI_IN_REWIND, midiIn->rewind));
- m_learners.push_back(new geMidiLearnerMaster(0, 0, LEARNER_WIDTH, "play/stop", G_MIDI_IN_START_STOP, midiIn->startStop));
- m_learners.push_back(new geMidiLearnerMaster(0, 0, LEARNER_WIDTH, "action recording", G_MIDI_IN_ACTION_REC, midiIn->actionRec));
- m_learners.push_back(new geMidiLearnerMaster(0, 0, LEARNER_WIDTH, "input recording", G_MIDI_IN_INPUT_REC, midiIn->inputRec));
- m_learners.push_back(new geMidiLearnerMaster(0, 0, LEARNER_WIDTH, "metronome", G_MIDI_IN_METRONOME, midiIn->volumeIn));
- m_learners.push_back(new geMidiLearnerMaster(0, 0, LEARNER_WIDTH, "input volume", G_MIDI_IN_VOLUME_IN, midiIn->volumeOut));
- m_learners.push_back(new geMidiLearnerMaster(0, 0, LEARNER_WIDTH, "output volume", G_MIDI_IN_VOLUME_OUT, midiIn->beatDouble));
- m_learners.push_back(new geMidiLearnerMaster(0, 0, LEARNER_WIDTH, "sequencer ×2", G_MIDI_IN_BEAT_DOUBLE, midiIn->beatHalf));
- m_learners.push_back(new geMidiLearnerMaster(0, 0, LEARNER_WIDTH, "sequencer ÷2", G_MIDI_IN_BEAT_HALF, midiIn->metronome));
- pack->end();
- m_ok = new geButton(w()-88, pack->y()+pack->h()+G_GUI_OUTER_MARGIN, 80, G_GUI_UNIT, "Close");
+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()
+: gdMidiInputBase(m::conf::conf.midiInputX, m::conf::conf.midiInputY, 300, 284, "MIDI Input Setup (global)")
+{
end();
- for (geMidiLearnerBase* l : m_learners)
- midiIn->enabled ? l->activate() : l->deactivate();
+ geGroup* groupHeader = new geGroup(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN);
+ m_enable = new geCheck(0, 0, 120, G_GUI_UNIT, "Enable MIDI input");
+ m_channel = new geChoice(m_enable->x() + m_enable->w() + 44, 0, 120, G_GUI_UNIT);
+ groupHeader->resizable(nullptr);
+ groupHeader->add(m_enable);
+ groupHeader->add(m_channel);
- m_ok->callback(cb_close, (void*)this);
+ 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, "Close");
- m_enable->value(midiIn->enabled);
+ add(groupHeader);
+ add(m_learners);
+ add(m_ok);
+
+ m_ok->callback(cb_close, (void*)this);
m_enable->callback(cb_enable, (void*)this);
m_channel->add("Channel (any)");
m_channel->add("Channel 14");
m_channel->add("Channel 15");
m_channel->add("Channel 16");
-
- m_channel->value(midiIn->filter - 1 ? 0 : midiIn->filter + 1);
m_channel->callback(cb_setChannel, (void*)this);
- midiIn->enabled ? m_channel->activate() : m_channel->deactivate();
-
- m::model::midiIn.unlock();
u::gui::setFavicon(this);
+
+ set_modal();
+ rebuild();
show();
}
/* -------------------------------------------------------------------------- */
-void gdMidiInputMaster::cb_enable(Fl_Widget* w, void* p) { ((gdMidiInputMaster*)p)->cb_enable(); }
-void gdMidiInputMaster::cb_setChannel(Fl_Widget* w, void* p) { ((gdMidiInputMaster*)p)->cb_setChannel(); }
+void gdMidiInputMaster::rebuild()
+{
+ m_data = c::io::master_getInputData();
+
+ m_enable->value(m_data.enabled);
+ m_channel->value(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_setChannel(Fl_Widget* /*w*/, void* p) { ((gdMidiInputMaster*)p)->cb_setChannel(); }
/* -------------------------------------------------------------------------- */
void gdMidiInputMaster::cb_enable()
{
- m::model::onSwap(m::model::midiIn, [&](m::model::MidiIn& m)
- {
- m.enabled = m_enable->value();
- });
-
- m_enable->value() ? m_channel->activate() : m_channel->deactivate();
-
- for (geMidiLearnerBase* l : m_learners)
- m_enable->value() ? l->activate() : l->deactivate();
+ c::io::master_enableMidiLearn(m_enable->value());
}
void gdMidiInputMaster::cb_setChannel()
{
- m::model::onSwap(m::model::midiIn, [&](m::model::MidiIn& m)
- {
- m.filter = m_channel->value() == 0 ? -1 : m_channel->value() - 1;
- });
+ c::io::master_setMidiFilter(m_channel->value() == 0 ? -1 : m_channel->value() - 1);
}
}} // giada::v::
#define GD_MIDI_INPUT_MASTER_H
-#include "core/model/model.h"
+#include "glue/io.h"
+#include "gui/elems/midiIO/midiLearnerPack.h"
#include "midiInputBase.h"
class geCheck;
-class geChoice;
namespace giada {
namespace v
{
+class geChoice;
+class geMasterLearnerPack : public geMidiLearnerPack
+{
+public:
+
+ geMasterLearnerPack(int x, int y);
+
+ void update(const c::io::Master_InputData&);
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+
class gdMidiInputMaster : public gdMidiInputBase
{
public:
gdMidiInputMaster();
+ void rebuild() override;
+
private:
- static void cb_enable(Fl_Widget* w, void* p);
- static void cb_setChannel(Fl_Widget* w, void* p);
+ static void cb_enable(Fl_Widget* /*w*/, void* p);
+ static void cb_setChannel(Fl_Widget* /*w*/, void* p);
void cb_enable();
void cb_setChannel();
+
+ c::io::Master_InputData m_data;
+
+ geMasterLearnerPack* m_learners;
};
}} // giada::v::
#include "glue/io.h"
+#include "gui/elems/midiIO/midiLearner.h"
#include "gui/elems/basics/check.h"
#include "midiOutputBase.h"
namespace giada {
namespace v
{
-gdMidiOutputBase::gdMidiOutputBase(int w, int h, ID channelId)
-: gdWindow (w, h, "Midi Output Setup"),
- m_channelId(channelId)
+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("playing", G_MIDI_OUT_L_PLAYING);
+ addMidiLearner("mute", G_MIDI_OUT_L_MUTE);
+ addMidiLearner("solo", G_MIDI_OUT_L_SOLO);
}
/* -------------------------------------------------------------------------- */
-gdMidiOutputBase::~gdMidiOutputBase()
+void geLightningLearnerPack::update(const c::io::Channel_OutputData& d)
{
- c::io::stopMidiLearn();
+ learners[0]->update(d.lightningPlaying);
+ learners[1]->update(d.lightningMute);
+ learners[2]->update(d.lightningSolo);
+ setEnabled(d.lightningEnabled);
}
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-void gdMidiOutputBase::refresh()
+gdMidiOutputBase::gdMidiOutputBase(int w, int h, ID channelId)
+: gdWindow (w, h, "Midi Output Setup")
+, m_channelId(channelId)
{
- for (geMidiLearnerBase* l : m_learners)
- l->refresh();
}
/* -------------------------------------------------------------------------- */
-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(); }
+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_enableLightning()
{
- m::model::onSwap(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- c.midiOutL = m_enableLightning->value();
- });
-
- for (geMidiLearnerBase* l : m_learners)
- m_enableLightning->value() ? l->activate() : l->deactivate();
+ c::io::channel_enableMidiLightning(m_channelId, m_enableLightning->value());
}
/* -------------------------------------------------------------------------- */
-void gdMidiOutputBase::setTitle(int chanNum)
+void gdMidiOutputBase::setTitle(ID channelId)
{
- std::string tmp = "MIDI Output Setup (channel " + std::to_string(chanNum) + ")";
+ std::string tmp = "MIDI Output Setup (channel " + std::to_string(channelId) + ")";
copy_label(tmp.c_str());
}
-
}} // giada::v::
#include "core/types.h"
+#include "glue/io.h"
+#include "gui/elems/midiIO/midiLearnerPack.h"
+#include "gui/elems/midiIO/midiLearner.h"
#include "gui/dialogs/window.h"
-#include "gui/elems/midiIO/midiLearnerBase.h"
class geButton;
Both MidiOutputMidiCh and MidiOutputSampleCh have the MIDI lighting widget set.
In addition MidiOutputMidiCh has the MIDI message output box. */
-/* TODO - gdMidiOutput is almost the same thing of gdMidiInput. Create another
-parent class gdMidiIO to inherit from */
-
namespace giada {
namespace v
{
-class geMidiLearner;
+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();
- void refresh() override;
-
protected:
/* cb_close
close current window. */
- static void cb_close(Fl_Widget* w, void* p);
+ static void cb_close(Fl_Widget* /*w*/, void* p);
void cb_close();
- static void cb_enableLightning(Fl_Widget* w, void* p);
+ static void cb_enableLightning(Fl_Widget* /*w*/, void* p);
void cb_enableLightning();
/* setTitle
* set window title. */
- void setTitle(int chanNum);
+ void setTitle(ID channelId);
ID m_channelId;
-
- std::vector<geMidiLearnerBase*> m_learners;
- geButton* m_close;
- geCheck* m_enableLightning;
+ c::io::Channel_OutputData m_data;
+
+ geLightningLearnerPack* m_learners;
+ geButton* m_close;
+ geCheck* m_enableLightning;
};
}} // giada::v::
* -------------------------------------------------------------------------- */
-#include "core/channels/midiChannel.h"
-#include "core/model/model.h"
-#include "utils/gui.h"
-#include "gui/elems/midiIO/midiLearnerChannel.h"
+#include <FL/Fl_Pack.H>
+#include "glue/io.h"
+#include "gui/elems/midiIO/midiLearner.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/check.h"
#include "gui/elems/basics/choice.h"
+#include "utils/gui.h"
#include "midiOutputMidiCh.h"
gdMidiOutputMidiCh::gdMidiOutputMidiCh(ID channelId)
: gdMidiOutputBase(300, 168, channelId)
{
- m::model::ChannelsLock l(m::model::channels);
- m::MidiChannel& c = static_cast<m::MidiChannel&>(m::model::get(m::model::channels, m_channelId));
-
+ end();
setTitle(m_channelId + 1);
- begin();
- m_enableOut = new geCheck(x()+8, y()+8, 150, 20, "Enable MIDI output");
- m_chanListOut = new geChoice(w()-108, y()+8, 100, 20);
+ m_enableOut = new geCheck(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, 150, G_GUI_UNIT, "Enable MIDI output");
+ m_chanListOut = new geChoice(w()-108, G_GUI_OUTER_MARGIN, 100, G_GUI_UNIT);
- m_enableLightning = new geCheck(x()+8, m_chanListOut->y()+m_chanListOut->h()+8, 120, 20, "Enable MIDI lightning output");
- m_learners.push_back(new geMidiLearnerChannel(x()+8, m_enableLightning->y()+m_enableLightning->h()+8,
- w()-16, "playing", G_MIDI_OUT_L_PLAYING, c.midiOutLplaying, m_channelId));
- m_learners.push_back(new geMidiLearnerChannel(x()+8, m_enableLightning->y()+m_enableLightning->h()+32,
- w()-16, "mute", G_MIDI_OUT_L_MUTE, c.midiOutLmute, m_channelId));
- m_learners.push_back(new geMidiLearnerChannel(x()+8, m_enableLightning->y()+m_enableLightning->h()+56,
- w()-16, "solo", G_MIDI_OUT_L_SOLO, c.midiOutLsolo, m_channelId));
+ m_enableLightning = new geCheck(G_GUI_OUTER_MARGIN, m_chanListOut->y() + m_chanListOut->h() + G_GUI_OUTER_MARGIN,
+ 120, G_GUI_UNIT, "Enable MIDI lightning output");
+
+ m_learners = new geLightningLearnerPack(G_GUI_OUTER_MARGIN,
+ m_enableLightning->y() + m_enableLightning->h() + G_GUI_OUTER_MARGIN, channelId);
+
+ m_close = new geButton(w() - 88, m_learners->y() + m_learners->h() + G_GUI_OUTER_MARGIN, 80, G_GUI_UNIT, "Close");
- m_close = new geButton(w()-88, m_enableLightning->y()+m_enableLightning->h()+84, 80, 20, "Close");
-
- end();
+ add(m_enableOut);
+ add(m_chanListOut);
+ add(m_enableLightning);
+ add(m_learners);
+ add(m_close);
m_chanListOut->add("Channel 1");
m_chanListOut->add("Channel 2");
m_chanListOut->add("Channel 16");
m_chanListOut->value(0);
- if (c.midiOut)
- m_enableOut->value(1);
- else
- m_chanListOut->deactivate();
-
- m_enableLightning->value(c.midiOutL);
- for (geMidiLearnerBase* l : m_learners)
- c.midiOutL ? l->activate() : l->deactivate();
-
- m_chanListOut->value(c.midiOutChan);
m_chanListOut->callback(cb_setChannel, (void*)this);
-
m_enableOut->callback(cb_enableOut, (void*)this);
m_enableLightning->callback(cb_enableLightning, (void*)this);
m_close->callback(cb_close, (void*)this);
- set_modal();
u::gui::setFavicon(this);
+
+ set_modal();
+ rebuild();
show();
}
/* -------------------------------------------------------------------------- */
-void gdMidiOutputMidiCh::cb_enableOut (Fl_Widget *w, void *p) { ((gdMidiOutputMidiCh*)p)->cb_enableOut(); }
-void gdMidiOutputMidiCh::cb_setChannel(Fl_Widget *w, void *p) { ((gdMidiOutputMidiCh*)p)->cb_setChannel(); }
+void gdMidiOutputMidiCh::rebuild()
+{
+ m_data = c::io::channel_getOutputData(m_channelId);
+
+ assert(m_data.output.has_value());
+
+ m_learners->update(m_data);
+ m_chanListOut->value(m_data.output->filter);
+ m_enableOut->value(m_data.output->enabled);
+
+ m_data.output->enabled ? m_chanListOut->activate() : m_chanListOut->deactivate();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdMidiOutputMidiCh::cb_enableOut (Fl_Widget* /*w*/, void* p) { ((gdMidiOutputMidiCh*)p)->cb_enableOut(); }
+void gdMidiOutputMidiCh::cb_setChannel(Fl_Widget* /*w*/, void* p) { ((gdMidiOutputMidiCh*)p)->cb_setChannel(); }
/* -------------------------------------------------------------------------- */
void gdMidiOutputMidiCh::cb_enableOut()
{
- m::model::onSwap(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- static_cast<m::MidiChannel&>(c).midiOut = m_enableOut->value();
- static_cast<m::MidiChannel&>(c).midiOutChan = m_chanListOut->value();
- });
-
- m_enableOut->value() ? m_chanListOut->activate() : m_chanListOut->deactivate();
+ c::io::channel_enableMidiOutput(m_channelId, m_enableOut->value());
}
void gdMidiOutputMidiCh::cb_setChannel()
{
- m::model::onSwap(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- static_cast<m::MidiChannel&>(c).midiOutChan = m_chanListOut->value();
- });
+ c::io::channel_setMidiOutputFilter(m_channelId, m_chanListOut->value());
}
}} // giada::v::
gdMidiOutputMidiCh(ID channelId);
+ void rebuild() override;
+
private:
- static void cb_enableOut (Fl_Widget* w, void* p);
- static void cb_setChannel(Fl_Widget* w, void* p);
+ static void cb_enableOut (Fl_Widget* /*w*/, void* p);
+ static void cb_setChannel(Fl_Widget* /*w*/, void* p);
void cb_enableOut();
void cb_setChannel();
* -------------------------------------------------------------------------- */
+#include <FL/Fl_Pack.H>
#include "core/model/model.h"
-#include "core/channels/sampleChannel.h"
#include "utils/gui.h"
-#include "gui/elems/midiIO/midiLearnerChannel.h"
+#include "gui/elems/midiIO/midiLearner.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/check.h"
#include "midiOutputSampleCh.h"
gdMidiOutputSampleCh::gdMidiOutputSampleCh(ID channelId)
: gdMidiOutputBase(300, 140, channelId)
{
- m::model::ChannelsLock l(m::model::channels);
- m::Channel& c = m::model::get(m::model::channels, m_channelId);
-
- setTitle(c.id);
+ end();
+ setTitle(m_channelId);
- m_enableLightning = new geCheck(8, 8, 120, 20, "Enable MIDI lightning output");
- m_learners.push_back(new geMidiLearnerChannel(8, m_enableLightning->y()+m_enableLightning->h()+8, w()-16, "playing",
- G_MIDI_OUT_L_PLAYING, c.midiOutLplaying, m_channelId));
- m_learners.push_back(new geMidiLearnerChannel(8, m_enableLightning->y()+m_enableLightning->h()+32, w()-16, "mute",
- G_MIDI_OUT_L_MUTE, c.midiOutLmute, m_channelId));
- m_learners.push_back(new geMidiLearnerChannel(8, m_enableLightning->y()+m_enableLightning->h()+56, w()-16, "solo",
- G_MIDI_OUT_L_SOLO, c.midiOutLsolo, m_channelId));
+ m_enableLightning = new geCheck(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, 120, 20, "Enable MIDI lightning output");
- m_close = new geButton(w()-88, m_enableLightning->y()+m_enableLightning->h()+84, 80, 20, "Close");
- m_close->callback(cb_close, (void*)this);
+ m_learners = new geLightningLearnerPack(G_GUI_OUTER_MARGIN,
+ m_enableLightning->y() + m_enableLightning->h() + 8, channelId);
+
+ m_close = new geButton(w() - 88, m_learners->y() + m_learners->h() + 8, 80, 20, "Close");
+
+ add(m_enableLightning);
+ add(m_learners);
+ add(m_close);
- m_enableLightning->value(c.midiOutL);
+ m_close->callback(cb_close, (void*)this);
m_enableLightning->callback(cb_enableLightning, (void*)this);
- for (geMidiLearnerBase* l : m_learners)
- c.midiOutL ? l->activate() : l->deactivate();
+ u::gui::setFavicon(this);
set_modal();
- u::gui::setFavicon(this);
+ rebuild();
show();
}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void gdMidiOutputSampleCh::rebuild()
+{
+ m_data = c::io::channel_getOutputData(m_channelId);
+
+ m_enableLightning->value(m_data.lightningEnabled);
+ m_learners->update(m_data);
+}
}} // giada::v::
public:
gdMidiOutputSampleCh(ID channelId);
+
+ void rebuild() override;
};
}} // giada::v::
#include "glue/plugin.h"
#include "utils/gui.h"
-#include "core/channels/channel.h"
#include "core/conf.h"
-#include "core/pluginManager.h"
-#include "core/pluginHost.h"
+#include "core/plugins/pluginManager.h"
+#include "core/plugins/pluginHost.h"
#include "gui/elems/plugin/pluginBrowser.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/choice.h"
namespace giada {
namespace v
{
-gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, ID chanID)
-: gdWindow(X, Y, W, H, "Available plugins"),
- m_chanID(chanID)
+gdPluginChooser::gdPluginChooser(int X, int Y, int W, int H, ID channelId)
+: gdWindow (X, Y, W, H, "Available plugins")
+, m_channelId(channelId)
{
/* top area */
Fl_Group *group_top = new Fl_Group(8, 8, w()-16, 20);
/* -------------------------------------------------------------------------- */
-void gdPluginChooser::cb_close(Fl_Widget* v, void* p) { ((gdPluginChooser*)p)->cb_close(); }
-void gdPluginChooser::cb_add(Fl_Widget* v, void* p) { ((gdPluginChooser*)p)->cb_add(); }
-void gdPluginChooser::cb_sort(Fl_Widget* v, void* p) { ((gdPluginChooser*)p)->cb_sort(); }
+void gdPluginChooser::cb_close(Fl_Widget* /*w*/, void* p) { ((gdPluginChooser*)p)->cb_close(); }
+void gdPluginChooser::cb_add (Fl_Widget* /*w*/, void* p) { ((gdPluginChooser*)p)->cb_add(); }
+void gdPluginChooser::cb_sort (Fl_Widget* /*w*/, void* p) { ((gdPluginChooser*)p)->cb_sort(); }
/* -------------------------------------------------------------------------- */
int pluginIndex = browser->value() - 3; // subtract header lines
if (pluginIndex < 0)
return;
- c::plugin::addPlugin(pluginIndex, m_chanID);
+ c::plugin::addPlugin(pluginIndex, m_channelId);
do_callback();
}
}} // giada::v::
#include "window.h"
-class geChoice;
class geButton;
class geButton;
namespace giada {
namespace v
{
+class geChoice;
class gePluginBrowser;
class gdPluginChooser : public gdWindow
{
public:
- gdPluginChooser(int x, int y, int w, int h, ID chanID);
+ gdPluginChooser(int x, int y, int w, int h, ID channelId);
~gdPluginChooser();
private:
- static void cb_close(Fl_Widget* w, void* p);
- static void cb_add (Fl_Widget* w, void* p);
- static void cb_sort (Fl_Widget* w, void* p);
+ static void cb_close(Fl_Widget* /*w*/, void* p);
+ static void cb_add (Fl_Widget* /*w*/, void* p);
+ static void cb_sort (Fl_Widget* /*w*/, void* p);
void cb_close();
void cb_add ();
void cb_sort ();
geButton* cancelBtn;
gePluginBrowser* browser;
- ID m_chanID;
+ ID m_channelId;
};
}} // giada::v::
#include <cassert>
#include <string>
-#include "core/model/model.h"
-#include "core/channels/channel.h"
#include "core/conf.h"
#include "core/const.h"
-#include "core/pluginHost.h"
#include "utils/string.h"
#include "utils/gui.h"
#include "gui/elems/basics/liquidScroll.h"
namespace giada {
namespace v
{
-gdPluginList::gdPluginList(ID chanID)
-: gdWindow(m::conf::conf.pluginListX, m::conf::conf.pluginListY, 468, 204),
- m_channelId(chanID)
+gdPluginList::gdPluginList(ID channelId)
+: gdWindow (m::conf::conf.pluginListX, m::conf::conf.pluginListY, 468, 204)
+, m_channelId(channelId)
{
end();
w() - (G_GUI_OUTER_MARGIN*2), h() - (G_GUI_OUTER_MARGIN*2));
list->end();
add(list);
-
- rebuild();
-
- if (m_channelId == m::mixer::MASTER_OUT_CHANNEL_ID)
- label("Master Out Plug-ins");
- else
- if (m_channelId == m::mixer::MASTER_IN_CHANNEL_ID)
- label("Master In Plug-ins");
- else {
- std::string l = "Channel " + u::string::iToString(m_channelId + 1) + " Plug-ins";
- copy_label(l.c_str());
- }
+ resizable(list);
u::gui::setFavicon(this);
set_non_modal();
+ rebuild();
show();
}
/* -------------------------------------------------------------------------- */
-void gdPluginList::cb_addPlugin(Fl_Widget* v, void* p) { ((gdPluginList*)p)->cb_addPlugin(); }
+void gdPluginList::cb_addPlugin(Fl_Widget* /*v*/, void* p) { ((gdPluginList*)p)->cb_addPlugin(); }
/* -------------------------------------------------------------------------- */
void gdPluginList::rebuild()
{
+ m_plugins = c::plugin::getPlugins(m_channelId);
+
+ if (m_plugins.channelId == m::mixer::MASTER_OUT_CHANNEL_ID)
+ label("Master Out Plug-ins");
+ else
+ if (m_plugins.channelId == m::mixer::MASTER_IN_CHANNEL_ID)
+ label("Master In Plug-ins");
+ else {
+ std::string l = "Channel " + u::string::iToString(m_plugins.channelId) + " Plug-ins";
+ copy_label(l.c_str());
+ }
+
/* Clear the previous list. */
list->clear();
list->scroll_to(0, 0);
- m::model::ChannelsLock l(m::model::channels);
-
- const m::Channel& ch = m::model::get(m::model::channels, m_channelId);
-
- for (ID pluginId : ch.pluginIds)
- list->addWidget(new gePluginElement(pluginId, m_channelId, 0, 0, 0));
+ for (ID pluginId : m_plugins.pluginIds)
+ list->addWidget(new gePluginElement(0, 0, c::plugin::getPlugin(pluginId, m_plugins.channelId)));
addPlugin = list->addWidget(new geButton(0, 0, 0, G_GUI_UNIT, "-- add new plugin --"));
int ww = m::conf::conf.pluginChooserW;
int wh = m::conf::conf.pluginChooserH;
u::gui::openSubWindow(G_MainWin, new v::gdPluginChooser(wx, wy, ww, wh,
- m_channelId), WID_FX_CHOOSER);
+ m_plugins.channelId), WID_FX_CHOOSER);
}
#define GD_PLUGINLIST_H
-#include "core/pluginHost.h"
+#include "glue/plugin.h"
#include "window.h"
{
public:
- gdPluginList(ID chanID);
+ gdPluginList(ID channelId);
~gdPluginList();
void rebuild() override;
private:
- static void cb_addPlugin(Fl_Widget* v, void* p);
+ static void cb_addPlugin(Fl_Widget* /*w*/, void* p);
void cb_addPlugin();
geButton* addPlugin;
geLiquidScroll* list;
ID m_channelId;
+ c::plugin::Plugins m_plugins;
};
-
}} // giada::v::
#include <FL/fl_draw.H>
+#include "glue/plugin.h"
#include "utils/gui.h"
-#include "core/plugin.h"
-#include "core/model/model.h"
#include "core/const.h"
#include "gui/elems/basics/liquidScroll.h"
#include "gui/elems/plugin/pluginParameter.h"
namespace giada {
namespace v
{
-gdPluginWindow::gdPluginWindow(ID pluginId)
-: gdWindow (450, 156),
- m_pluginId(pluginId)
+gdPluginWindow::gdPluginWindow(const c::plugin::Plugin& plugin)
+: gdWindow(450, 156)
+, m_plugin(plugin)
{
set_non_modal();
m_list = new geLiquidScroll(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN,
w()-(G_GUI_OUTER_MARGIN*2), h()-(G_GUI_OUTER_MARGIN*2));
-
- m::model::PluginsLock l(m::model::plugins);
- const m::Plugin& p = m::model::get(m::model::plugins, m_pluginId);
m_list->type(Fl_Scroll::VERTICAL_ALWAYS);
m_list->begin();
- int labelWidth = getLabelWidth();
- int numParams = p.getNumParameters();
- for (int i=0; i<numParams; i++) {
- int py = m_list->y() + (i * (G_GUI_UNIT + G_GUI_INNER_MARGIN));
+ 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(i, m_pluginId, m_list->x(), py, pw, labelWidth);
+ new v::gePluginParameter(m_list->x(), py, pw, labelWidth, c::plugin::getParam(index, m_plugin.id));
}
m_list->end();
end();
- label(p.getName().c_str());
+ label(m_plugin.name.c_str());
size_range(450, (G_GUI_UNIT + (G_GUI_OUTER_MARGIN*2)));
resizable(m_list);
/* -------------------------------------------------------------------------- */
-void gdPluginWindow::updateParameter(int index, bool changeSlider)
-{
- static_cast<v::gePluginParameter*>(m_list->child(index))->update(changeSlider);
-}
-
-
void gdPluginWindow::updateParameters(bool changeSlider)
{
- m::model::onGet(m::model::plugins, m_pluginId, [&](m::Plugin& p)
- {
- for (int i=0; i<p.getNumParameters(); i++) {
- static_cast<v::gePluginParameter*>(m_list->child(i))->update(changeSlider);
- }
- });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-int gdPluginWindow::getLabelWidth() const
-{
- m::model::PluginsLock l(m::model::plugins);
- const m::Plugin& p = m::model::get(m::model::plugins, m_pluginId);
-
- int width = 0;
- int numParams = p.getNumParameters();
- for (int i=0; i<numParams; i++) {
- int wl = 0, hl = 0;
- fl_measure(p.getParameterName(i).c_str(), wl, hl);
- if (wl > width)
- width = wl;
- }
- return width;
+ for (int index : m_plugin.paramIndexes)
+ static_cast<v::gePluginParameter*>(m_list->child(index))->update(c::plugin::getParam(index, m_plugin.id), changeSlider);
}
}} // giada::v::
namespace giada {
+namespace c {
+namespace plugin
+{
+class Plugin;
+}}
namespace m
{
class Plugin;
{
public:
- gdPluginWindow(ID pluginId);
+ gdPluginWindow(const c::plugin::Plugin&);
- void updateParameter(int index, bool changeSlider=false);
void updateParameters(bool changeSlider=false);
private:
-
- int getLabelWidth() const;
- ID m_pluginId;
+ const c::plugin::Plugin& m_plugin;
geLiquidScroll* m_list;
};
#include <FL/x.H>
#include "utils/log.h"
#include "utils/gui.h"
-#include "core/pluginHost.h"
-#include "core/model/model.h"
-#include "core/plugin.h"
+#include "glue/plugin.h"
#include "core/const.h"
#include "pluginWindowGUI.h"
#ifdef G_OS_MAC
namespace giada {
namespace v
{
-gdPluginWindowGUI::gdPluginWindowGUI(ID pluginId)
+gdPluginWindowGUI::gdPluginWindowGUI(c::plugin::Plugin& p)
#ifdef G_OS_MAC
-: gdWindow (Fl::w(), Fl::h()),
+: gdWindow(Fl::w(), Fl::h())
#else
-: gdWindow (320, 200),
+: gdWindow(320, 200)
#endif
- m_pluginId(pluginId),
- m_ui (nullptr)
+, m_plugin(p)
+, m_ui (nullptr)
{
show();
resize((Fl::w() - pluginW) / 2, (Fl::h() - pluginH) / 2, pluginW, pluginH);
+ m_plugin.setResizeCallback([this] (int w, int h)
+ {
+ resize(x(), y(), w, h);
+ });
#endif
+#ifdef G_OS_LINUX
Fl::add_timeout(G_GUI_PLUGIN_RATE, cb_refresh, (void*) this);
+#endif
- m::model::onGet(m::model::plugins, m_pluginId, [&](m::Plugin& p)
- {
- copy_label(p.getName().c_str());
- });
+ copy_label(m_plugin.name.c_str());
}
/* -------------------------------------------------------------------------- */
-void gdPluginWindowGUI::cb_close(Fl_Widget* v, void* p) { ((gdPluginWindowGUI*)p)->cb_close(); }
+void gdPluginWindowGUI::cb_close(Fl_Widget* /*v*/, void* p) { ((gdPluginWindowGUI*)p)->cb_close(); }
void gdPluginWindowGUI::cb_refresh(void* data) { ((gdPluginWindowGUI*)data)->cb_refresh(); }
void gdPluginWindowGUI::cb_close()
{
+#ifdef G_OS_LINUX
Fl::remove_timeout(cb_refresh);
+#endif
closeEditor();
u::log::print("[gdPluginWindowGUI::__cb_close] GUI closed, this=%p\n", (void*) this);
}
void gdPluginWindowGUI::openEditor(void* parent)
{
- m::model::onGet(m::model::plugins, m_pluginId, [&](m::Plugin& p)
- {
- m_ui = p.createEditor();
- });
+ m_ui = m_plugin.createEditor();
if (m_ui == nullptr) {
u::log::print("[gdPluginWindowGUI::openEditor] unable to create editor!\n");
return;
delete m_ui;
m_ui = nullptr;
}
-
}} // giada::v::
namespace giada {
+namespace c {
+namespace plugin
+{
+struct Plugin;
+}}
namespace v
{
class gdPluginWindowGUI : public gdWindow
{
public:
- gdPluginWindowGUI(ID pluginId);
+ gdPluginWindowGUI(c::plugin::Plugin&);
~gdPluginWindowGUI();
private:
- static void cb_close(Fl_Widget* v, void* p);
+ static void cb_close(Fl_Widget* /*w*/, void* p);
static void cb_refresh(void* data);
void cb_close();
void cb_refresh();
void openEditor(void* parent);
void closeEditor();
- ID m_pluginId;
+ c::plugin::Plugin& m_plugin;
juce::AudioProcessorEditor* m_ui;
};
#include <FL/Fl_Group.H>
#include "glue/channel.h"
#include "glue/sampleEditor.h"
-#include "core/model/model.h"
-#include "core/channels/sampleChannel.h"
#include "core/waveFx.h"
#include "core/conf.h"
#include "core/const.h"
#include "gui/elems/basics/dial.h"
#include "gui/elems/basics/box.h"
#include "gui/elems/basics/check.h"
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/group.h"
#include "gui/elems/sampleEditor/waveform.h"
#include "gui/elems/sampleEditor/waveTools.h"
#include "gui/elems/sampleEditor/volumeTool.h"
namespace giada {
namespace v
{
-gdSampleEditor::gdSampleEditor(ID channelId, ID waveId)
+gdSampleEditor::gdSampleEditor(ID channelId)
: gdWindow (m::conf::conf.sampleEditorX, m::conf::conf.sampleEditorY,
- m::conf::conf.sampleEditorW, m::conf::conf.sampleEditorH),
- m_channelId(channelId),
- m_waveId (waveId)
+ m::conf::conf.sampleEditorW, m::conf::conf.sampleEditorH)
+, m_channelId(channelId)
{
- Fl_Group* upperBar = createUpperBar();
+ end();
+
+ gePack* upperBar = createUpperBar();
- waveTools = new geWaveTools(channelId, waveId, G_GUI_OUTER_MARGIN, upperBar->y()+upperBar->h()+G_GUI_OUTER_MARGIN,
- w()-16, h()-128);
+ waveTools = new geWaveTools(G_GUI_OUTER_MARGIN, upperBar->y()+upperBar->h()+G_GUI_OUTER_MARGIN,
+ w()-16, h()-168);
- Fl_Group* bottomBar = createBottomBar(G_GUI_OUTER_MARGIN, waveTools->y()+waveTools->h()+G_GUI_OUTER_MARGIN,
+ gePack* bottomBar = createBottomBar(G_GUI_OUTER_MARGIN, waveTools->y()+waveTools->h()+G_GUI_OUTER_MARGIN,
h()-waveTools->h()-upperBar->h()-32);
- end();
-
add(upperBar);
add(waveTools);
add(bottomBar);
m::conf::conf.sampleEditorGridVal = atoi(grid->text());
m::conf::conf.sampleEditorGridOn = snap->value();
- c::sampleEditor::setPreview(m_channelId, PreviewMode::NONE);
+ c::sampleEditor::stopPreview();
+ c::sampleEditor::cleanupPreview();
}
void gdSampleEditor::rebuild()
{
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- copy_label(c.name.c_str());
- });
-
- volumeTool->rebuild();
- waveTools->rebuild();
- panTool->rebuild();
- pitchTool->rebuild();
- rangeTool->rebuild();
- shiftTool->rebuild();
-
- m::model::onGet(m::model::waves, m_waveId, [&](m::Wave& w)
- {
- updateInfo(w);
- if (w.isLogical()) // Logical samples (aka takes) cannot be reloaded.
- reload->deactivate();
- });
+ m_data = c::sampleEditor::getData(m_channelId);
+
+ copy_label(m_data.name.c_str());
+
+ 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();
-
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- play->setStatus(c.previewMode == PreviewMode::LOOP || c.previewMode == PreviewMode::NORMAL ? 1 : 0);
- });
+ play->setStatus(m_data.a_getPreviewStatus() == ChannelStatus::PLAY);
}
/* -------------------------------------------------------------------------- */
-Fl_Group* gdSampleEditor::createUpperBar()
+gePack* gdSampleEditor::createUpperBar()
{
- Fl_Group* g = new Fl_Group(G_GUI_OUTER_MARGIN, G_GUI_OUTER_MARGIN, w()-16, G_GUI_UNIT);
- g->begin();
- grid = new geChoice(g->x(), g->y(), 50, G_GUI_UNIT);
- snap = new geCheck(grid->x()+grid->w()+4, g->y(), 12, G_GUI_UNIT, "Snap");
- sep1 = new geBox(snap->x()+snap->w()+4, g->y(), g->w() - 118, G_GUI_UNIT);
- zoomOut = new geButton(sep1->x()+sep1->w()+4, g->y(), G_GUI_UNIT, G_GUI_UNIT, "", zoomOutOff_xpm, zoomOutOn_xpm);
- zoomIn = new geButton(zoomOut->x()+zoomOut->w()+4, g->y(), G_GUI_UNIT, G_GUI_UNIT, "", zoomInOff_xpm, zoomInOn_xpm);
- g->end();
- g->resizable(sep1);
+ reload = new geButton(0, 0, 70, G_GUI_UNIT, "Reload");
+ grid = new geChoice(0, 0, 50, G_GUI_UNIT);
+ snap = new geCheck (0, 0, 12, G_GUI_UNIT, "Snap");
+ sep1 = new geBox (0, 0, w() - 208, G_GUI_UNIT);
+ 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->add("(off)");
grid->add("2");
zoomOut->callback(cb_zoomOut, (void*)this);
zoomIn->callback(cb_zoomIn, (void*)this);
+ 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;
}
-
+\
/* -------------------------------------------------------------------------- */
+\
-
-Fl_Group* gdSampleEditor::createOpTools(int x, int y, int h)
+gePack* gdSampleEditor::createOpTools(int x, int y)
{
- Fl_Group* g = new Fl_Group(x, y, 572, h);
- g->begin();
- g->resizable(0);
- volumeTool = new geVolumeTool(m_channelId, g->x(), g->y());
- panTool = new gePanTool(m_channelId, volumeTool->x()+volumeTool->w()+4, g->y());
-
- pitchTool = new gePitchTool(m_channelId, g->x(), panTool->y()+panTool->h()+8);
-
- rangeTool = new geRangeTool(m_channelId, m_waveId, g->x(), pitchTool->y()+pitchTool->h()+8);
- shiftTool = new geShiftTool(m_channelId, m_waveId, rangeTool->x()+rangeTool->w()+4, pitchTool->y()+pitchTool->h()+8);
- reload = new geButton(g->x()+g->w()-70, shiftTool->y(), 70, 20, "Reload");
- g->end();
-
- reload->callback(cb_reload, (void*)this);
+ 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;
}
/* -------------------------------------------------------------------------- */
-Fl_Group* gdSampleEditor::createPreviewBox(int x, int y, int h)
+geGroup* gdSampleEditor::createPreviewBox(int x, int y, int h)
{
- Fl_Group* g = new Fl_Group(x, y, 110, h);
- g->begin();
- rewind = new geButton(g->x(), g->y()+(g->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(), 12, 25, "Loop");
- g->end();
+ 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, "Loop");
play->callback(cb_togglePreview, (void*)this);
rewind->callback(cb_rewindPreview, (void*)this);
- return g;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-Fl_Group* gdSampleEditor::createInfoBox(int x, int y, int h)
-{
- Fl_Group* g = new Fl_Group(x, y, 400, h);
- g->begin();
- info = new geBox(g->x(), g->y(), g->w(), g->h());
- g->end();
-
- info->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_TOP);
+ geGroup* g = new geGroup(x, y);
+ g->add(rewind);
+ g->add(play);
+ g->add(loop);
return g;
}
/* -------------------------------------------------------------------------- */
-Fl_Group* gdSampleEditor::createBottomBar(int x, int y, int h)
+gePack* gdSampleEditor::createBottomBar(int x, int y, int h)
{
- Fl_Group* g = new Fl_Group(8, waveTools->y()+waveTools->h()+8, w()-16, h);
- g->begin();
- Fl_Group* previewBox = createPreviewBox(g->x(), g->y(), g->h());
+ geGroup* previewBox = createPreviewBox(0, 0, h);
+ geBox* divisor1 = new geBox (0, 0, 1, h);
+ Fl_Group* opTools = createOpTools (0, 0);
+ geBox* divisor2 = new geBox (0, 0, 1, h);
+ info = new geBox (0, 0, 400, h);
- geBox* divisor1 = new geBox(previewBox->x()+previewBox->w()+8, g->y(), 1, g->h());
- divisor1->box(FL_BORDER_BOX);
+ divisor1->box(FL_BORDER_BOX);
+ divisor2->box(FL_BORDER_BOX);
- Fl_Group* opTools = createOpTools(divisor1->x()+divisor1->w()+12, g->y(), g->h());
-
- geBox* divisor2 = new geBox(opTools->x()+opTools->w()+8, g->y(), 1, g->h());
- divisor2->box(FL_BORDER_BOX);
-
- createInfoBox(divisor2->x()+divisor2->w()+8, g->y(), g->h());
+ info->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE | FL_ALIGN_TOP);
- g->end();
+ 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_changeGrid (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->cb_changeGrid(); }
-void gdSampleEditor::cb_enableSnap (Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->cb_enableSnap(); }
-void gdSampleEditor::cb_togglePreview(Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->cb_togglePreview(); }
-void gdSampleEditor::cb_rewindPreview(Fl_Widget* w, void* p) { ((gdSampleEditor*)p)->cb_rewindPreview(); }
+void gdSampleEditor::cb_reload (Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_reload(); }
+void gdSampleEditor::cb_zoomIn (Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_zoomIn(); }
+void gdSampleEditor::cb_zoomOut (Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_zoomOut(); }
+void gdSampleEditor::cb_changeGrid (Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_changeGrid(); }
+void gdSampleEditor::cb_enableSnap (Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_enableSnap(); }
+void gdSampleEditor::cb_togglePreview(Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_togglePreview(); }
+void gdSampleEditor::cb_rewindPreview(Fl_Widget* /*w*/, void* p) { ((gdSampleEditor*)p)->cb_rewindPreview(); }
/* -------------------------------------------------------------------------- */
void gdSampleEditor::cb_togglePreview()
{
- if (play->getStatus())
- c::sampleEditor::setPreview(m_channelId, PreviewMode::NONE);
+ if (!play->getStatus())
+ c::sampleEditor::playPreview(loop->value());
else
- c::sampleEditor::setPreview(m_channelId, loop->value() ? PreviewMode::LOOP : PreviewMode::NORMAL);
+ c::sampleEditor::stopPreview();
}
void gdSampleEditor::cb_rewindPreview()
{
- c::sampleEditor::rewindPreview(m_channelId);
+ c::sampleEditor::setPreviewTracker(m_data.begin);
}
void gdSampleEditor::cb_reload()
{
- c::sampleEditor::reload(m_channelId, m_waveId);
+ c::sampleEditor::reload(m_data.channelId, m_data.waveId);
redraw();
}
/* -------------------------------------------------------------------------- */
-void gdSampleEditor::updateInfo(const m::Wave& w)
+void gdSampleEditor::updateInfo()
{
- std::string bitDepth = w.getBits() != 0 ? u::string::iToString(w.getBits()) : "(unknown)";
+ std::string bitDepth = m_data.waveBits != 0 ? u::string::iToString(m_data.waveBits) : "(unknown)";
std::string infoText =
- "File: " + w.getPath() + "\n"
- "Size: " + u::string::iToString(w.getSize()) + " frames\n"
- "Duration: " + u::string::iToString(w.getDuration()) + " seconds\n"
+ "File: " + m_data.wavePath + "\n"
+ "Size: " + u::string::iToString(m_data.waveSize) + " frames\n"
+ "Duration: " + u::string::iToString(m_data.waveDuration) + " seconds\n"
"Bit depth: " + bitDepth + "\n"
- "Frequency: " + u::string::iToString(w.getRate()) + " Hz\n";
+ "Frequency: " + u::string::iToString(m_data.waveRate) + " Hz\n";
info->copy_label(infoText.c_str());
}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void gdSampleEditor::setWaveId(ID id)
-{
- m_waveId = id;
- waveTools->waveId = id;
- waveTools->waveform->setWaveId(id);
-}
}} // giada::v::
#include "core/types.h"
+#include "glue/sampleEditor.h"
#include "window.h"
class geButton;
-class geChoice;
class geCheck;
class geBox;
class geButton;
namespace giada {
namespace m
{
-class SampleChannel;
class Wave;
}
namespace v
{
+class geChoice;
+class gePack;
+class geGroup;
class geVolumeTool;
class geWaveTools;
class geBoostTool;
class gePitchTool;
class geRangeTool;
class geShiftTool;
-
class gdSampleEditor : public gdWindow
{
friend class geWaveform;
public:
- gdSampleEditor(ID channelId, ID waveId);
+ gdSampleEditor(ID channelId);
~gdSampleEditor();
void rebuild() override;
void refresh() override;
- void updateInfo(const m::Wave& w);
- void setWaveId(ID id);
-
geChoice* grid;
geCheck* snap;
geBox* sep1;
private:
- Fl_Group* createUpperBar();
- Fl_Group* createBottomBar(int x, int y, int h);
-
- Fl_Group* createPreviewBox(int x, int y, int h);
- Fl_Group* createOpTools(int x, int y, int h);
- Fl_Group* createInfoBox(int x, int y, int h);
-
- static void cb_reload (Fl_Widget* w, void* p);
- static void cb_zoomIn (Fl_Widget* w, void* p);
- static void cb_zoomOut (Fl_Widget* w, void* p);
- static void cb_changeGrid(Fl_Widget* w, void* p);
- static void cb_enableSnap(Fl_Widget* w, void* p);
- static void cb_togglePreview(Fl_Widget* w, void* p);
- static void cb_rewindPreview(Fl_Widget* w, void* p);
+ 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_changeGrid(Fl_Widget* /*w*/, void* p);
+ static void cb_enableSnap(Fl_Widget* /*w*/, void* p);
+ static void cb_togglePreview(Fl_Widget* /*w*/, void* p);
+ static void cb_rewindPreview(Fl_Widget* /*w*/, void* p);
void cb_reload();
void cb_zoomIn();
void cb_zoomOut();
void cb_enableSnap();
void cb_togglePreview();
void cb_rewindPreview();
-
+
+ void updateInfo();
+
ID m_channelId;
- ID m_waveId;
+
+ c::sampleEditor::Data m_data;
};
}} // giada::v::
namespace giada {
namespace v
{
-void cb_window_closer(Fl_Widget* v, void* p)
+void cb_window_closer(Fl_Widget* /*v*/, void* p)
{
delete (Fl_Window*) p;
}
{
/* delete all subwindows in order to empty the stack */
- for (unsigned i=0; i<subWindows.size(); i++)
- delete subWindows.at(i);
+ 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* v, void* p)
+void gdWindow::cb_closeChild(Fl_Widget* w, void* /*p*/)
{
- gdWindow* child = (gdWindow*) v;
+ gdWindow* child = (gdWindow*) w;
if (child->getParent() != nullptr)
(child->getParent())->delSubWindow(child);
}
void gdWindow::addSubWindow(gdWindow* w)
{
- /** TODO - useless: delete ---------------------------------------- */
- for (unsigned i=0; i<subWindows.size(); i++)
- if (w->getId() == subWindows.at(i)->getId()) {
- //u::log::print("[gdWindow] window %p (id=%d) exists, not added (and deleted)\n", (void*)w, w->getId());
- delete w;
- return;
- }
- /** --------------------------------------------------------------- */
-
w->setParent(this);
w->callback(cb_closeChild); // you can pass params: w->callback(cb_closeChild, (void*)params)
subWindows.push_back(w);
void gdWindow::delSubWindow(gdWindow* w)
{
- for (unsigned i=0; i<subWindows.size(); i++)
- if (w->getId() == subWindows.at(i)->getId()) {
- delete subWindows.at(i);
- subWindows.erase(subWindows.begin() + i);
- //debug();
+ 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;
}
- //debug();
}
/* -------------------------------------------------------------------------- */
-void gdWindow::delSubWindow(int id)
+void gdWindow::delSubWindow(int wid)
{
- for (unsigned i=0; i<subWindows.size(); i++)
- if (subWindows.at(i)->getId() == id) {
- delete subWindows.at(i);
- subWindows.erase(subWindows.begin() + i);
- //debug();
+ for (unsigned j = 0; j < subWindows.size(); j++)
+ if (subWindows.at(j)->getId() == wid) {
+ delete subWindows.at(j);
+ subWindows.erase(subWindows.begin() + j);
return;
}
- //debug();
}
}
-void gdWindow::setId(int id)
+void gdWindow::setId(int wid)
{
- this->id = id;
+ id = wid;
}
void gdWindow::debug() const
{
+ /* TODO - use G_DEBUG
u::log::print("---- window stack (id=%d): ----\n", getId());
for (unsigned i=0; i<subWindows.size(); i++)
u::log::print("[gdWindow] %p (id=%d)\n", (void*)subWindows.at(i), subWindows.at(i)->getId());
u::log::print("----\n");
+ */
}
/* -------------------------------------------------------------------------- */
-bool gdWindow::hasWindow(int id) const
+bool gdWindow::hasWindow(int wid) const
{
- for (unsigned i=0; i<subWindows.size(); i++)
- if (id == subWindows.at(i)->getId())
+ for (unsigned j = 0; j < subWindows.size(); j++)
+ if (wid == subWindows.at(j)->getId())
return true;
return false;
}
/* -------------------------------------------------------------------------- */
-gdWindow* gdWindow::getChild(int id)
+gdWindow* gdWindow::getChild(int wid)
{
- for (unsigned i=0; i<subWindows.size(); i++)
- if (id == subWindows.at(i)->getId())
- return subWindows.at(i);
+ for (unsigned j = 0; j < subWindows.size(); j++)
+ if (wid == subWindows.at(j)->getId())
+ return subWindows.at(j);
return nullptr;
}
}} // giada::v::
\ No newline at end of file
/* cb_window_closer
Callback for closing windows. Deletes the widget (delete). */
-void cb_window_closer(Fl_Widget* v, void* p);
+void cb_window_closer(Fl_Widget* /*w*/, void* p);
class gdWindow : public Fl_Double_Window
{
gdWindow(int w, int h, const char* title=0, int id=0);
~gdWindow();
- static void cb_closeChild(Fl_Widget* v, void* p);
+ static void cb_closeChild(Fl_Widget* /*w*/, void* p);
/* rebuild, refresh
Rebuild() is called by the View Updater when something structural changes
#include <cassert>
#include <FL/Fl.H>
-#include "core/channels/channel.h"
#include "core/init.h"
-#include "core/const.h"
-#include "core/mixer.h"
-#include "core/mixerHandler.h"
-#include "core/recManager.h"
-#include "core/conf.h"
-#include "glue/io.h"
-#include "glue/main.h"
+#include "glue/events.h"
#include "gui/dialogs/mainWindow.h"
#include "gui/elems/mainWindow/keyboard/channel.h"
#include "gui/elems/mainWindow/keyboard/keyboard.h"
/* -------------------------------------------------------------------------- */
-void perform_(const geChannel* gch, int event)
+void perform_(ID channelId, int event)
{
- if (event == FL_KEYDOWN)
- c::io::keyPress(gch->channelId, Fl::event_ctrl(), Fl::event_shift(), G_MAX_VELOCITY);
+ 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::io::keyRelease(gch->channelId, Fl::event_ctrl(), Fl::event_shift());
+ c::events::releaseChannel(channelId, Thread::MAIN);
}
G_MainWin->keyboard->forEachChannel([=](geChannel& c)
{
if (c.handleKey(event))
- perform_(&c, event);
+ perform_(c.getData().id, event);
});
}
void dispatchKey(int event)
{
/* These events come from the keyboard, not from a direct interaction on the
- UI with the mouse/touch. So the 'gui' parameter is set to false. */
+ UI with the mouse/touch. */
if (event == FL_KEYDOWN) {
if (Fl::event_key() == FL_BackSpace && !backspace_) {
backspace_ = true;
- m::mh::rewindSequencer();
+ c::events::rewindSequencer(Thread::MAIN);
}
else if (Fl::event_key() == FL_End && !end_) {
end_ = true;
- c::main::toggleInputRec();
+ c::events::toggleInputRecording();
}
else if (Fl::event_key() == FL_Enter && !enter_) {
enter_ = true;
- m::recManager::toggleActionRec(static_cast<RecTriggerMode>(m::conf::conf.recTriggerMode));
+ c::events::toggleActionRecording();
}
else if (Fl::event_key() == ' ' && !space_) {
space_ = true;
- m::mh::toggleSequencer();
+ c::events::toggleSequencer(Thread::MAIN);
}
else if (Fl::event_key() == FL_Escape && !esc_) {
esc_ = true;
/* -------------------------------------------------------------------------- */
-void dispatchTouch(const geChannel* gch, bool status)
+void dispatchTouch(const geChannel& gch, bool status)
{
triggerSignalCb_();
- perform_(gch, status ? FL_KEYDOWN : FL_KEYUP);
+ perform_(gch.getData().id, status ? FL_KEYDOWN : FL_KEYUP);
}
/* dispatchTouch
Processes a mouse click/touch event. */
-void dispatchTouch(const geChannel* gch, bool status);
+void dispatchTouch(const geChannel& gch, bool status);
void setSignalCallback(std::function<void()> f);
}}} // giada::v::dispatcher
{
geBaseAction::geBaseAction(Pixel X, Pixel Y, Pixel W, Pixel H, bool resizable,
m::Action a1, m::Action a2)
-: Fl_Box (X, Y, W, H),
- m_resizable(resizable),
- onRightEdge(false),
- onLeftEdge (false),
- hovered (false),
- altered (false),
- pick (0),
- a1 (a1),
- a2 (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());
namespace giada {
namespace v
{
-geBaseActionEditor::geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h)
-: Fl_Group(x, y, w, h),
- m_base (static_cast<gdBaseActionEditor*>(window())),
- m_action(nullptr)
+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++) {
+ for (int i = 0; i < children(); i++) {
geBaseAction* a = static_cast<geBaseAction*>(child(i));
if (a->hovered)
return a;
{
public:
- geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h);
-
- /* updateActions
- Rebuild the actions widgets from scratch. */
+ /* updateActions
+ Rebuild the actions widgets from scratch. */
- virtual void rebuild() = 0;
+ virtual void rebuild(c::actionEditor::Data& d) = 0;
/* handle
Override base FL_Group events. */
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
#include <FL/fl_draw.H>
#include "utils/log.h"
#include "utils/math.h"
-#include "core/channels/sampleChannel.h"
#include "core/const.h"
#include "core/conf.h"
#include "core/action.h"
#include "core/recorder.h"
#include "glue/actionEditor.h"
+#include "glue/channel.h"
#include "gui/dialogs/actionEditor/baseActionEditor.h"
#include "envelopePoint.h"
#include "envelopeEditor.h"
namespace giada {
namespace v
{
-geEnvelopeEditor::geEnvelopeEditor(Pixel x, Pixel y, const char* l)
-: geBaseActionEditor(x, y, 200, m::conf::conf.envelopeEditorH)
+geEnvelopeEditor::geEnvelopeEditor(Pixel x, Pixel y, const char* l, gdBaseActionEditor* b)
+: geBaseActionEditor(x, y, 200, m::conf::conf.envelopeEditorH, b)
{
copy_label(l);
}
/* -------------------------------------------------------------------------- */
-void geEnvelopeEditor::rebuild()
+void geEnvelopeEditor::rebuild(c::actionEditor::Data& d)
{
- namespace mr = m::recorder;
- namespace ca = c::actionEditor;
+ 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_base->getActions()) {
+ 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));
Frame f = m_base->pixelToFrame(Fl::event_x() - x());
int v = yToValue(Fl::event_y() - y());
- c::actionEditor::recordEnvelopeAction(m_base->channelId, f, v);
+ c::actionEditor::recordEnvelopeAction(m_data->channelId, f, v);
- m_base->rebuild();
+ m_base->rebuild(); // TODO - USELESS
}
void geEnvelopeEditor::onDeleteAction()
{
- c::actionEditor::deleteEnvelopeAction(m_base->channelId, m_action->a1);
+ c::actionEditor::deleteEnvelopeAction(m_data->channelId, m_action->a1);
- m_base->rebuild();
+ m_base->rebuild(); // TODO - USELESS
}
{
Frame f = m_base->pixelToFrame((m_action->x() - x()) + geEnvelopePoint::SIDE / 2);
float v = yToValue(m_action->y() - y(), geEnvelopePoint::SIDE);
- c::actionEditor::updateEnvelopeAction(m_base->channelId, m_action->a1, f, v);
+ c::actionEditor::updateEnvelopeAction(m_data->channelId, m_action->a1, f, v);
m_base->rebuild();
}
namespace v
{
class geEnvelopePoint;
-
-
class geEnvelopeEditor : public geBaseActionEditor
{
public:
- geEnvelopeEditor(Pixel x, Pixel y, const char* l);
+ geEnvelopeEditor(Pixel x, Pixel y, const char* l, gdBaseActionEditor*);
~geEnvelopeEditor();
void draw() override;
- void rebuild() override;
+ void rebuild(c::actionEditor::Data& d) override;
private:
/* -------------------------------------------------------------------------- */
-void geGridTool::cb_changeType(Fl_Widget *w, void *p) { ((geGridTool*)p)->cb_changeType(); }
+void geGridTool::cb_changeType(Fl_Widget* /*w*/, void* p) { ((geGridTool*)p)->cb_changeType(); }
/* -------------------------------------------------------------------------- */
#include "core/types.h"
-class geChoice;
class geCheck;
namespace giada {
namespace v
{
+class geChoice;
class geGridTool : public Fl_Group
{
public:
geChoice* gridType;
geCheck* active;
- static void cb_changeType(Fl_Widget* w, void* p);
+ static void cb_changeType(Fl_Widget* /*w*/, void* p);
void cb_changeType();
};
}} // giada::v::
#include <FL/Fl.H>
-#include "core/channels/midiChannel.h"
#include "core/const.h"
#include "core/conf.h"
#include "gui/dialogs/actionEditor/midiActionEditor.h"
namespace v
{
geNoteEditor::geNoteEditor(Pixel x, Pixel y, gdMidiActionEditor* base)
-: geScroll(x, y, 200, 422),
- m_base (base)
+: geScroll(x, y, 200, 422)
+, m_base (base)
{
- pianoRoll = new gePianoRoll(x, y, m_base->fullWidth);
-
- size(m_base->fullWidth, m::conf::conf.pianoRollH);
+ end();
type(Fl_Scroll::VERTICAL_ALWAYS);
+ size(m_base->fullWidth, m::conf::conf.pianoRollH);
+
+ pianoRoll = new gePianoRoll(x, y, m_base->fullWidth, base);
+ add(pianoRoll);
}
/* -------------------------------------------------------------------------- */
-void geNoteEditor::rebuild()
+void geNoteEditor::rebuild(c::actionEditor::Data& d)
{
size(m_base->fullWidth, h());
- pianoRoll->rebuild();
+ pianoRoll->rebuild(d);
}
}} // giada::v::
\ No newline at end of file
{
class gdMidiActionEditor;
class gePianoRoll;
-
-
class geNoteEditor : public geScroll
{
public:
geNoteEditor(Pixel x, Pixel y, gdMidiActionEditor* base);
~geNoteEditor();
- void rebuild();
+ void rebuild(c::actionEditor::Data& d);
void scroll();
gePianoRoll* pianoRoll;
#include <cassert>
#include <FL/Fl.H>
-#include "core/channels/midiChannel.h"
#include "core/conf.h"
#include "core/const.h"
#include "core/clock.h"
#include "utils/log.h"
#include "utils/string.h"
#include "utils/math.h"
+#include "glue/channel.h"
#include "glue/actionEditor.h"
#include "gui/dialogs/actionEditor/baseActionEditor.h"
#include "pianoItem.h"
namespace giada {
namespace v
{
-gePianoRoll::gePianoRoll(Pixel X, Pixel Y, Pixel W)
- : geBaseActionEditor(X, Y, W, 40),
- pick (0)
+gePianoRoll::gePianoRoll(Pixel X, Pixel Y, Pixel W, gdBaseActionEditor* b)
+: geBaseActionEditor(X, Y, W, 40, b)
+, pick (0)
{
position(x(), m::conf::conf.pianoRollY == -1 ? y()-(h()/2) : m::conf::conf.pianoRollY);
}
int octave = MAX_OCTAVES;
- for (int i=1; i<=MAX_KEYS+1; i++) {
+ for (int i = 1; i <= MAX_KEYS+1; i++) {
/* print key note label. C C# D D# E F F# G G# A A# B */
{
fl_copy_offscreen(x(), y(), CELL_W, h(), surface1, 0, 0);
-#if defined(__APPLE__) // TODO - is this still useful?
- for (Pixel i=36; i<m_base->fullWidth; i+=36) /// TODO: i < ae->coverX is faster
+// TODO - is this APPLE thing still useful?
+#if defined(__APPLE__)
+ for (Pixel i = 36; i < m_base->fullWidth; i += 36) /// TODO: i < m_base->loopWidth is faster
fl_copy_offscreen(x()+i, y(), CELL_W, h(), surface2, 1, 0);
#else
- for (Pixel i=CELL_W; i<m_base->loopWidth; i+=CELL_W)
+ for (Pixel i = CELL_W; i < m_base->loopWidth; i += CELL_W)
fl_copy_offscreen(x()+i, y(), CELL_W, h(), surface2, 0, 0);
#endif
{
Frame frame = m_base->pixelToFrame(Fl::event_x() - x());
int note = yToNote(Fl::event_y() - y());
- c::actionEditor::recordMidiAction(m_base->channelId, note, G_MAX_VELOCITY,
+ 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_base->channelId, m_action->a1);
+ c::actionEditor::deleteMidiAction(m_data->channelId, m_action->a1);
m_base->rebuild(); // Rebuild velocityEditor as well
}
int note = yToNote(m_action->y() - y());
int velocity = m_action->a1.event.getVelocity();
- ca::updateMidiAction(m_base->channelId, m_action->a1, note, velocity, f1, f2);
+ ca::updateMidiAction(m_data->channelId, m_action->a1, note, velocity, f1, f2);
m_base->rebuild(); // Rebuild velocityEditor as well
}
/* -------------------------------------------------------------------------- */
-void gePianoRoll::rebuild()
+void gePianoRoll::rebuild(c::actionEditor::Data& d)
{
- namespace ca = c::actionEditor;
+ 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_base->getActions())
+ for (const m::Action& a1 : m_data->actions)
{
if (a1.event.getStatus() == m::MidiEvent::NOTE_OFF)
continue;
static const Pixel CELL_H = 20;
static const Pixel CELL_W = 40;
- gePianoRoll(Pixel x, Pixel y, Pixel w);
+ gePianoRoll(Pixel x, Pixel y, Pixel w, gdBaseActionEditor*);
void draw() override;
int handle(int e) override;
- void rebuild() override;
+ void rebuild(c::actionEditor::Data& d) override;
Pixel pick;
#include <FL/fl_draw.H>
-#include "core/channels/sampleChannel.h"
#include "core/const.h"
#include "core/action.h"
#include "sampleAction.h"
#include <cassert>
#include <FL/Fl.H>
#include <FL/fl_draw.H>
-#include "core/channels/sampleChannel.h"
-#include "core/model/model.h"
#include "core/recorder.h"
#include "core/const.h"
#include "core/conf.h"
#include "core/action.h"
#include "utils/log.h"
#include "glue/actionEditor.h"
+#include "glue/channel.h"
#include "gui/dialogs/actionEditor/baseActionEditor.h"
#include "sampleAction.h"
#include "sampleActionEditor.h"
namespace giada {
namespace v
{
-geSampleActionEditor::geSampleActionEditor(Pixel x, Pixel y)
-: geBaseActionEditor(x, y, 200, m::conf::conf.sampleActionEditorH)
+geSampleActionEditor::geSampleActionEditor(Pixel x, Pixel y, gdBaseActionEditor* b)
+: geBaseActionEditor(x, y, 200, m::conf::conf.sampleActionEditorH, b)
{
}
/* -------------------------------------------------------------------------- */
-void geSampleActionEditor::rebuild()
+void geSampleActionEditor::rebuild(c::actionEditor::Data& d)
{
- namespace mr = m::recorder;
- namespace ca = c::actionEditor;
+ m_data = &d;
- bool isSinglePressMode;
- bool isAnyLoopMode;
-
- m::model::onGet(m::model::channels, m_base->channelId, [&](m::Channel& c)
- {
- const m::SampleChannel& sc = static_cast<m::SampleChannel&>(c);
- isSinglePressMode = sc.mode == ChannelMode::SINGLE_PRESS;
- isAnyLoopMode = sc.isAnyLoopMode();
- });
+ 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_base->getActions()) {
+ for (const m::Action& a1 : m_data->actions) {
if (a1.event.getStatus() == m::MidiEvent::ENVELOPE || isNoteOffSinglePress(a1))
continue;
- m::Action a2 = a1.next != nullptr ? *a1.next : m::Action{};
+ const m::Action& a2 = a1.next != nullptr ? *a1.next : m::Action{};
Pixel px = x() + m_base->frameToPixel(a1.frame);
Pixel py = y() + 4;
void geSampleActionEditor::onAddAction()
{
Frame f = m_base->pixelToFrame(Fl::event_x() - x());
- c::actionEditor::recordSampleAction(m_base->channelId, m_base->getActionType(), f);
-
- m_base->rebuild();
+ c::actionEditor::recordSampleAction(m_data->channelId, m_base->getActionType(), f);
}
void geSampleActionEditor::onDeleteAction()
{
- c::actionEditor::deleteSampleAction(m_base->channelId, m_action->a1);
-
- m_base->rebuild();
+ c::actionEditor::deleteSampleAction(m_data->channelId, m_action->a1);
}
f2 = m_base->pixelToFrame(p2);
}
- ca::updateSampleAction(m_base->channelId, m_action->a1, type, f1, f2);
+ ca::updateSampleAction(m_data->channelId, m_action->a1, type, f1, f2);
m_base->rebuild();
}
bool geSampleActionEditor::isNoteOffSinglePress(const m::Action& a)
{
- bool res;
- m::model::onGet(m::model::channels, m_base->channelId, [&](m::Channel& c)
- {
- const m::SampleChannel& sc = static_cast<m::SampleChannel&>(c);
- res = sc.mode == ChannelMode::SINGLE_PRESS && a.event.getStatus() == m::MidiEvent::NOTE_OFF;
- });
- return res;
+ return m_data->sample->channelMode == SamplePlayerMode::SINGLE_PRESS &&
+ a.event.getStatus() == m::MidiEvent::NOTE_OFF;
+
}
}} // giada::v::
{
public:
- geSampleActionEditor(Pixel x, Pixel y);
+ geSampleActionEditor(Pixel x, Pixel y, gdBaseActionEditor*);
~geSampleActionEditor();
void draw() override;
- void rebuild() override;
+ void rebuild(c::actionEditor::Data& d) override;
private:
#include <FL/fl_draw.H>
#include "utils/log.h"
#include "utils/math.h"
-#include "core/channels/midiChannel.h"
#include "core/const.h"
#include "core/conf.h"
#include "core/action.h"
namespace giada {
namespace v
{
-geVelocityEditor::geVelocityEditor(Pixel x, Pixel y)
-: geBaseActionEditor(x, y, 200, m::conf::conf.velocityEditorH)
+geVelocityEditor::geVelocityEditor(Pixel x, Pixel y, gdBaseActionEditor* b)
+: geBaseActionEditor(x, y, 200, m::conf::conf.velocityEditorH, b)
{
}
/* -------------------------------------------------------------------------- */
-void geVelocityEditor::rebuild()
+void geVelocityEditor::rebuild(c::actionEditor::Data& d)
{
- namespace ca = c::actionEditor;
+ 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_base->getActions())
- {
+ for (const m::Action& action : m_data->actions) {
+
if (action.event.getStatus() == m::MidiEvent::NOTE_OFF)
continue;
{
public:
- geVelocityEditor(Pixel x, Pixel y);
+ geVelocityEditor(Pixel x, Pixel y, gdBaseActionEditor*);
~geVelocityEditor();
void draw() override;
- void rebuild() override;
+ void rebuild(c::actionEditor::Data& d) override;
private:
void onMoveAction() override;
void onRefreshAction() override;
- void onAddAction() override{};
- void onDeleteAction() override{};
- void onResizeAction() override{};
+ void onAddAction() override {};
+ void onDeleteAction() override {};
+ void onResizeAction() override {};
Pixel valueToY(int v) const;
int yToValue(Pixel y) const;
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * geBaseButton
- * Base class for every button widget.
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <FL/fl_draw.H>
-#include "baseButton.h"
-
-
-geBaseButton::geBaseButton(int x, int y, int w, int h, const char* l)
-: Fl_Button(x, y, w, h, l)
-{
- initLabel = l != nullptr ? l : "";
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geBaseButton::trimLabel()
-{
- if (initLabel.empty())
- return;
-
- std::string out;
- if (w() > 20) {
- out = initLabel;
- int len = initLabel.size();
- while (fl_width(out.c_str(), out.size()) > w()) {
- out = initLabel.substr(0, len) + "...";
- len--;
- }
- }
- else {
- out = "";
- }
- copy_label(out.c_str());
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geBaseButton::label(const char* l)
-{
- Fl_Button::label(l);
- initLabel = l;
- trimLabel();
-}
-
-
-const char* geBaseButton::label()
-{
- return Fl_Button::label();
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geBaseButton::draw()
-{
- trimLabel();
- Fl_Button::draw();
-}
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * geBaseButton
- * Base class for every button widget.
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_BASE_BUTTON_H
-#define GE_BASE_BUTTON_H
-
-
-#include <string>
-#include <FL/Fl_Button.H>
-
-
-class geBaseButton : public Fl_Button
-{
-public:
-
- geBaseButton(int x, int y, int w, int h, const char* l=nullptr);
-
- void draw() override;
- void label(const char* l);
- const char* label();
-
-private:
-
- std::string initLabel;
-
- void trimLabel();
-};
-
-
-#endif
* -------------------------------------------------------------------------- */
-#include "../../../core/const.h"
+#include <FL/fl_draw.H>
+#include "core/const.h"
+#include "utils/gui.h"
#include "box.h"
-geBox::geBox(int x, int y, int w, int h, const char *l, Fl_Align al)
-: Fl_Box(x, y, w, h)
+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);
- labelsize(G_GUI_FONT_SIZE_BASE);
- box(FL_NO_BOX);
- labelcolor(G_COLOR_LIGHT_2);
- if (al != 0)
- align(al | FL_ALIGN_INSIDE);
+ copy_label(l);
+ box(FL_NO_BOX);
+ align(al | FL_ALIGN_INSIDE);
}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geBox::draw()
+{
+ fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_1); // Clear background
+
+ 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(G_COLOR_LIGHT_2);
+ fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE);
+ fl_draw(giada::u::gui::truncate(label(), w()-8).c_str(), x()+4, y(), w()-4, h(), align());
+ }
+}
\ No newline at end of file
{
public:
- geBox(int x, int y, int w, int h, const char *l=0, Fl_Align al=FL_ALIGN_CENTER);
+ geBox(int x, int y, int w, int h, const char* l=nullptr, Fl_Align al=FL_ALIGN_CENTER);
+
+ void draw() override;
};
}
-void g_customUpBox(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*/)
{
fl_color(G_COLOR_GREY_2);
fl_rectf(x, y, w, h);
#include <FL/Fl.H>
-#define G_CUSTOM_BORDER_BOX FL_FREE_BOXTYPE
-#define G_CUSTOM_UP_BOX (Fl_Boxtype)(FL_FREE_BOXTYPE + 1)
-#define G_CUSTOM_DOWN_BOX (Fl_Boxtype)(FL_FREE_BOXTYPE + 3)
+constexpr Fl_Boxtype G_CUSTOM_BORDER_BOX = FL_FREE_BOXTYPE;
+constexpr Fl_Boxtype G_CUSTOM_UP_BOX = static_cast<Fl_Boxtype>(FL_FREE_BOXTYPE + 1);
+constexpr Fl_Boxtype G_CUSTOM_DOWN_BOX = static_cast<Fl_Boxtype>(FL_FREE_BOXTYPE + 3);
void g_customBorderBox(int x, int y, int w, int h, Fl_Color c);
#include <FL/fl_draw.H>
#include "core/const.h"
+#include "utils/gui.h"
#include "button.h"
geButton::geButton(int x, int y, int w, int h, const char* l,
const char** imgOff, const char** imgOn, const char** imgDisabled)
-: geBaseButton(x, y, w, h, l),
- imgOff (imgOff),
- imgOn (imgOn),
- imgDisabled (imgDisabled),
- bgColor0 (G_COLOR_GREY_2),
- bgColor1 (G_COLOR_GREY_4),
- bdColor (G_COLOR_GREY_4),
- txtColor (G_COLOR_LIGHT_2)
+: Fl_Button (x, y, w, h, l),
+ imgOff (imgOff),
+ imgOn (imgOn),
+ imgDisabled(imgDisabled),
+ bgColor0 (G_COLOR_GREY_2),
+ bgColor1 (G_COLOR_GREY_4),
+ bdColor (G_COLOR_GREY_4),
+ txtColor (G_COLOR_LIGHT_2)
{
}
void geButton::draw()
{
- geBaseButton::draw();
+ //Fl_Button::draw();
if (active())
if (value()) draw(imgOn, bgColor1, txtColor);
if (img != nullptr) {
fl_draw_pixmap(img, x()+1, y()+1);
+ return;
}
- else {
- fl_rectf(x()+1, y()+1, w()-2, h()-2, bgColor); // draw background
- fl_color(textColor);
+
+ 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(label(), x()+2, y(), w()-2, h(), FL_ALIGN_CENTER);
+ fl_draw(giada::u::gui::truncate(label(), w()-16).c_str(), x()+2, y(), w()-2, h(), FL_ALIGN_CENTER);
}
}
\ No newline at end of file
#define GE_BUTTON_H
-#include "baseButton.h"
+#include <FL/Fl_Button.H>
-class geButton : public geBaseButton
+class geButton : public Fl_Button
{
public:
* -------------------------------------------------------------------------- */
+#include <cstring>
#include <FL/fl_draw.H>
#include "core/const.h"
#include "check.h"
void geCheck::draw()
{
- int color = !active() ? FL_INACTIVE_COLOR : G_COLOR_GREY_4;
-
- if (value()) {
- fl_rect(x(), y(), 12, h(), (Fl_Color) color);
- fl_rectf(x(), y(), 12, h(), (Fl_Color) color);
- }
- else {
- fl_rectf(x(), y(), 12, h(), FL_BACKGROUND_COLOR);
- fl_rect(x(), y(), 12, h(), (Fl_Color) color);
- }
-
- fl_rectf(x()+20, y(), w(), h(), FL_BACKGROUND_COLOR); // clearer
+ fl_rectf(x(), y(), w(), h(), FL_BACKGROUND_COLOR); // clearer
+
+ const int boxColor = !active() ? FL_INACTIVE_COLOR : G_COLOR_GREY_4;
+ const int textColor = !active() ? FL_INACTIVE_COLOR : G_COLOR_LIGHT_2;
+ const int textAlign = hasMultilineText() ? FL_ALIGN_LEFT | FL_ALIGN_TOP : FL_ALIGN_LEFT | FL_ALIGN_CENTER;
+
+ if (value())
+ fl_rectf(x(), y(), 12, h(), (Fl_Color) boxColor);
+ else
+ fl_rect(x(), y(), 12, h(), (Fl_Color) boxColor);
+
fl_font(FL_HELVETICA, G_GUI_FONT_SIZE_BASE);
- fl_color(!active() ? FL_INACTIVE_COLOR : G_COLOR_LIGHT_2);
- fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) (FL_ALIGN_LEFT | FL_ALIGN_CENTER));
+ fl_color(textColor);
+ fl_draw(label(), x()+20, y(), w(), h(), (Fl_Align) textAlign);
}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+bool geCheck::hasMultilineText() const
+{
+ return label() == nullptr ? false : std::strchr(label(), '\n') != nullptr;
+}
+
geCheck(int x, int y, int w, int h, const char *l=0);
void draw() override;
+
+private:
+
+ bool hasMultilineText() const;
};
#include <string>
#include <FL/fl_draw.H>
-#include "../../../core/const.h"
+#include "core/const.h"
+#include "utils/gui.h"
+#include "utils/vector.h"
#include "choice.h"
-geChoice::geChoice(int x, int y, int w, int h, const char *l, bool ang)
- : Fl_Choice(x, y, w, h, l), angle(ang)
+namespace giada {
+namespace v
{
- 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);
+geChoice::geChoice(int x, int y, int w, int h, const char* l, bool ang)
+: Fl_Choice(x, y, w, h, l)
+, angle (ang)
+{
+ 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::cb_onChange(Fl_Widget* /*w*/, void* p) { (static_cast<geChoice*>(p))->cb_onChange(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChoice::cb_onChange()
+{
+ if (onChange != nullptr) onChange(getSelectedId());
}
void geChoice::draw()
{
- fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_2); // bg
- fl_rect(x(), y(), w(), h(), (Fl_Color) G_COLOR_GREY_4); // border
- if (angle)
- fl_polygon(x()+w()-8, y()+h()-1, x()+w()-1, y()+h()-8, x()+w()-1, y()+h()-1);
-
- /* pick up the text() from the selected item (value()) and print it in
- * the box and avoid overflows */
-
- fl_color(!active() ? G_COLOR_GREY_4 : G_COLOR_LIGHT_2);
- if (value() != -1) {
- if (fl_width(text(value())) < w()-8) {
- fl_draw(text(value()), x(), y(), w(), h(), FL_ALIGN_CENTER);
- }
- else {
- std::string tmp = text(value());
- int size = tmp.size();
- while (fl_width(tmp.c_str()) >= w()-16) {
- tmp.resize(size);
- size--;
- }
- tmp += "...";
- fl_draw(tmp.c_str(), x(), y(), w(), h(), FL_ALIGN_CENTER);
- }
-
- }
+ fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_2); // bg
+ fl_rect(x(), y(), w(), h(), (Fl_Color) G_COLOR_GREY_4); // border
+ if (angle)
+ fl_polygon(x()+w()-8, y()+h()-1, x()+w()-1, y()+h()-8, x()+w()-1, y()+h()-1);
+
+ /* pick up the text() from the selected item (value()) and print it in
+ * the box and avoid overflows */
+
+ fl_color(!active() ? G_COLOR_GREY_4 : G_COLOR_LIGHT_2);
+ if (value() != -1)
+ fl_draw(u::gui::truncate(text(value()), w()-16).c_str(), x(), y(), w(), h(), FL_ALIGN_CENTER);
}
/* -------------------------------------------------------------------------- */
-void geChoice::showItem(const char *c)
+ID geChoice::getSelectedId() const
+{
+ return value() == -1 ? 0 : ids.at(value());
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChoice::addItem(const std::string& label, ID id)
+{
+ Fl_Choice::add(label.c_str(), 0, cb_onChange, static_cast<void*>(this));
+ ids.push_back(id);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geChoice::showItem(const char* c)
+{
+ value(find_index(c));
+}
+
+
+void geChoice::showItem(ID id)
{
- value(find_index(c));
+ value(u::vector::indexOf(ids, id));
}
+}}
\ No newline at end of file
#define GE_CHOICE_H
+#include <functional>
+#include <string>
+#include <vector>
#include <FL/Fl_Choice.H>
+#include "core/types.h"
+namespace giada {
+namespace v
+{
class geChoice : public Fl_Choice
{
public:
- geChoice(int X,int Y,int W,int H,const char *L=0, bool angle=true);
- void draw();
+ geChoice(int x, int y, int w, int h, const char* l=0, bool angle=true);
+ void draw() override;
+
+ ID getSelectedId() const;
+
+ void addItem(const std::string& label, ID id);
+ void showItem(const char* c);
+ void showItem(ID id);
- void showItem(const char *c);
+ std::function<void(ID)> onChange = nullptr;
+
+private:
+
+ static void cb_onChange(Fl_Widget* /*w*/, void* p);
+ void cb_onChange();
bool angle;
- int id;
+ std::vector<ID> ids;
};
-
+}}
#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geScroll
+ * Custom scroll with nice scrollbars and something else.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <algorithm>
+#include "group.h"
+
+
+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
+}
+}}
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geScroll
+ * Custom scroll with nice scrollbars and something else.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_GROUP_H
+#define GE_GROUP_H
+
+
+#include <vector>
+#include <FL/Fl_Group.H>
+
+
+namespace giada {
+namespace v
+{
+/* geGroup
+A group that resizes itself accoring to the content. */
+
+class geGroup : public Fl_Group
+{
+public:
+
+ geGroup(int x, int y);
+
+ /* countChildren
+ 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). */
+
+ 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<Fl_Widget*> m_widgets;
+};
+}}
+
+
+#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geScroll
+ * Custom scroll with nice scrollbars and something else.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include "core/const.h"
+#include "pack.h"
+
+
+namespace giada {
+namespace 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);
+}
+}}
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * geScroll
+ * Custom scroll with nice scrollbars and something else.
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_PACK_H
+#define GE_PACK_H
+
+
+#include "core/const.h"
+#include "gui/elems/basics/group.h"
+
+
+namespace giada {
+namespace v
+{
+enum class Direction { HORIZONTAL, VERTICAL };
+
+/* gePack
+A stack of widgets that resize itself according to its content. */
+
+class gePack : public geGroup
+{
+public:
+
+ gePack(int x, int y, Direction d, int gutter=G_GUI_INNER_MARGIN);
+
+ /* add
+ Adds a Fl_Widget 'w' to this pack. Coordinates are relative to the group,
+ so origin starts at (0, 0). */
+
+ void add(Fl_Widget* w);
+
+private:
+
+ Direction m_direction;
+ int m_gutter;
+};
+}}
+
+
+#endif
geScroll::geScroll(int x, int y, int w, int h, int t)
- : Fl_Scroll(x, y, w, h)
+: Fl_Scroll(x, y, w, h)
{
+ end();
type(t);
scrollbar.color(G_COLOR_GREY_2);
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cassert>
+#include "core/const.h"
+#include "boxtypes.h"
+#include "scrollPack.h"
+
+
+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
+}
+}}
\ No newline at end of file
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_SCROLL_PACK_H
+#define GE_SCROLL_PACK_H
+
+
+#include "gui/elems/basics/scroll.h"
+#include "gui/elems/basics/pack.h"
+
+
+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<Fl_Widget*> m_widgets;
+
+ Direction m_direction;
+ int m_gutter;
+};
+}}
+
+
+#endif
std::string geBrowser::getCurrentDir()
{
- return normalize(u::string::getRealPath(m_currentDir));
+ return normalize(u::fs::getRealPath(m_currentDir));
}
#else
std::string sep = G_SLASH_STR;
#endif
- return normalize(u::string::getRealPath(m_currentDir + sep + normalize(text(value()))));
+ return normalize(u::fs::getRealPath(m_currentDir + sep + normalize(text(value()))));
}
}
begin();
soundsys = new geChoice(x()+114, y()+9, 250, 20, "System");
buffersize = new geChoice(x()+114, y()+37, 55, 20, "Buffer size");
- samplerate = new geChoice(x()+309, y()+37, 55, 20, "Sample rate");
+ samplerate = new geChoice(x()+304, y()+37, 60, 20, "Sample rate");
sounddevOut = new geChoice(x()+114, y()+65, 222, 20, "Output device");
devOutInfo = new geButton(x()+344, y()+65, 20, 20, "?");
channelsOut = new geChoice(x()+114, y()+93, 55, 20, "Output channels");
break;
}
-#elif defined (__APPLE__)
+#elif defined(__APPLE__)
if (m::kernelAudio::hasAPI(RtAudio::MACOSX_CORE))
soundsys->add("CoreAudio");
if (m::conf::conf.soundSystem != G_SYS_API_NONE) {
fetchSoundDevs();
- fetchOutChans(sounddevOut->value());
+ fetchOutChans();
fetchInChans(sounddevIn->value());
/* fill frequency dropdown menu */
buffersize->add("1024");
buffersize->add("2048");
buffersize->add("4096");
- buffersize->showItem(u::string::iToString(m::conf::conf.buffersize).c_str());
+ buffersize->showItem(std::to_string(m::conf::conf.buffersize).c_str());
rsmpQuality->add("Sinc best quality (very slow)");
rsmpQuality->add("Sinc medium quality (slow)");
/* -------------------------------------------------------------------------- */
-void geTabAudio::cb_deactivate_sounddev(Fl_Widget* w, void* p) { ((geTabAudio*)p)->cb_deactivate_sounddev(); }
-void geTabAudio::cb_fetchInChans(Fl_Widget* w, void* p) { ((geTabAudio*)p)->cb_fetchInChans(); }
-void geTabAudio::cb_fetchOutChans(Fl_Widget* w, void* p) { ((geTabAudio*)p)->cb_fetchOutChans(); }
-void geTabAudio::cb_showInputInfo(Fl_Widget* w, void* p) { ((geTabAudio*)p)->cb_showInputInfo(); }
-void geTabAudio::cb_showOutputInfo(Fl_Widget* w, void* p) { ((geTabAudio*)p)->cb_showOutputInfo(); }
+void geTabAudio::cb_deactivate_sounddev(Fl_Widget* /*w*/, void* p) { ((geTabAudio*)p)->cb_deactivate_sounddev(); }
+void geTabAudio::cb_fetchInChans(Fl_Widget* /*w*/, void* p) { ((geTabAudio*)p)->cb_fetchInChans(); }
+void geTabAudio::cb_fetchOutChans(Fl_Widget* /*w*/, void* p) { ((geTabAudio*)p)->cb_fetchOutChans(); }
+void geTabAudio::cb_showInputInfo(Fl_Widget* /*w*/, void* p) { ((geTabAudio*)p)->cb_showInputInfo(); }
+void geTabAudio::cb_showOutputInfo(Fl_Widget* /*w*/, void* p) { ((geTabAudio*)p)->cb_showOutputInfo(); }
/* -------------------------------------------------------------------------- */
void geTabAudio::cb_fetchOutChans()
{
- fetchOutChans(sounddevOut->value());
+ fetchOutChans();
channelsOut->value(0);
}
/* the '?' button is added by fetchSoundDevs */
- fetchOutChans(sounddevOut->value());
+ fetchOutChans();
sounddevOut->activate();
channelsOut->activate();
channelsIn->value(0);
return;
}
- for (unsigned i=0; i<chs; i+=2) {
- std::string tmp = u::string::iToString(i+1) + "-" + u::string::iToString(i+2);
- channelsIn->add(tmp.c_str());
- }
- channelsIn->value(m::conf::conf.channelsIn);
+
+ /* Dirty trick for stereo inputs: indexes start from 1000. */
+
+ for (unsigned i = 0; i < chs; i++)
+ channelsIn->addItem(std::to_string(i + 1).c_str(), i + 1);
+ for (unsigned i = 0; i < chs; i += 2)
+ channelsIn->addItem((std::to_string(i + 1) + "-" + std::to_string(i + 2)).c_str(), i + 1001);
+
+ if (m::conf::conf.channelsInCount == 1)
+ channelsIn->showItem(m::conf::conf.channelsInStart + 1);
+ else
+ channelsIn->showItem(m::conf::conf.channelsInStart + 1001);
}
/* -------------------------------------------------------------------------- */
-void geTabAudio::fetchOutChans(int menuItem)
+void geTabAudio::fetchOutChans()
{
channelsOut->clear();
else if (text == "WASAPI")
m::conf::conf.soundSystem = G_SYS_API_WASAPI;
-#elif defined (__APPLE__)
+#elif defined(__APPLE__)
else if (text == "CoreAudio")
m::conf::conf.soundSystem = G_SYS_API_CORE;
/* use the device name to search into the drop down menu's */
- m::conf::conf.soundDeviceOut = m::kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
- m::conf::conf.soundDeviceIn = m::kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
- m::conf::conf.channelsOut = channelsOut->value();
- m::conf::conf.channelsIn = channelsIn->value();
- m::conf::conf.limitOutput = limitOutput->value();
- m::conf::conf.rsmpQuality = rsmpQuality->value();
+ m::conf::conf.soundDeviceOut = m::kernelAudio::getDeviceByName(sounddevOut->text(sounddevOut->value()));
+ m::conf::conf.soundDeviceIn = m::kernelAudio::getDeviceByName(sounddevIn->text(sounddevIn->value()));
+ m::conf::conf.channelsOut = channelsOut->value();
+ m::conf::conf.channelsInCount = channelsIn->getSelectedId() < 1000 ? 1 : 2;
+ m::conf::conf.channelsInStart = channelsIn->getSelectedId() - (m::conf::conf.channelsInCount == 1 ? 1 : 1001);
+ m::conf::conf.limitOutput = limitOutput->value();
+ m::conf::conf.rsmpQuality = rsmpQuality->value();
- /* if sounddevOut is disabled (because of system change e.g. alsa ->
- * jack) its value is equal to -1. Change it! */
+ /* If sounddevOut is disabled because of system change e.g. alsa -> jack,
+ soundDeviceOut and channelsOut are == -1. Change them! */
- if (m::conf::conf.soundDeviceOut == -1)
+ if (m::conf::conf.soundDeviceOut == -1) {
m::conf::conf.soundDeviceOut = 0;
+ m::conf::conf.channelsOut = 0;
+ }
- m::conf::conf.buffersize = std::atoi(buffersize->text());
+ m::conf::conf.buffersize = std::atoi(buffersize->text());
m::conf::conf.recTriggerLevel = std::atof(recTriggerLevel->value());
const Fl_Menu_Item* i = nullptr;
#include <FL/Fl_Group.H>
-class geChoice;
class geCheck;
class geButton;
class geInput;
namespace giada {
namespace v
{
+class geChoice;
class geTabAudio : public Fl_Group
{
public:
private:
- static void cb_deactivate_sounddev(Fl_Widget* w, void* p);
- static void cb_fetchInChans (Fl_Widget* w, void* p);
- static void cb_fetchOutChans (Fl_Widget* w, void* p);
- static void cb_showInputInfo (Fl_Widget* w, void* p);
- static void cb_showOutputInfo (Fl_Widget* w, void* p);
+ static void cb_deactivate_sounddev(Fl_Widget* /*w*/, void* p);
+ static void cb_fetchInChans (Fl_Widget* /*w*/, void* p);
+ static void cb_fetchOutChans (Fl_Widget* /*w*/, void* p);
+ static void cb_showInputInfo (Fl_Widget* /*w*/, void* p);
+ static void cb_showOutputInfo (Fl_Widget* /*w*/, void* p);
void cb_deactivate_sounddev();
void cb_fetchInChans();
void cb_fetchOutChans();
void fetchSoundDevs();
void fetchInChans(int menuItem);
- void fetchOutChans(int menuItem);
+ void fetchOutChans();
int findMenuDevice(geChoice* m, int device);
int soundsysInitValue;
#include "core/const.h"
#include "core/conf.h"
#include "gui/elems/basics/box.h"
-#include "gui/elems/basics/radio.h"
#include "gui/elems/basics/check.h"
#include "tabBehaviors.h"
namespace giada {
namespace v
{
-geTabBehaviors::geTabBehaviors(int X, int Y, int W, int H)
-: Fl_Group(X, Y, W, H, "Behaviors")
+geTabBehaviors::geTabBehaviors(int X, int Y, int W, int H)
+: Fl_Group (X, Y, W, H)
+, m_container (X, Y + G_GUI_OUTER_MARGIN, Direction::VERTICAL, G_GUI_OUTER_MARGIN)
+, m_chansStopOnSeqHalt (0, 0, 280, 30, "Dynamic channels stop immediately when the sequencer\nis halted")
+, m_treatRecsAsLoops (0, 0, 280, 20, "Treat one shot channels with actions as loops")
+, m_inputMonitorDefaultOn (0, 0, 280, 20, "New sample channels have input monitor on by default")
+, m_overdubProtectionDefaultOn(0, 0, 280, 30, "New sample channels have overdub protection on\nby default")
{
- begin();
-
- Fl_Group* radioGrp_1 = new Fl_Group(x(), y()+10, w(), 70); // radio group for the mutex
- new geBox(x(), y()+10, 70, 25, "When a channel with recorded actions is halted:", FL_ALIGN_LEFT);
- recsStopOnChanHalt_1 = new geRadio(x()+25, y()+35, 280, 20, "stop it immediately");
- recsStopOnChanHalt_0 = new geRadio(x()+25, y()+60, 280, 20, "play it until finished");
- radioGrp_1->end();
-
- Fl_Group* radioGrp_2 = new Fl_Group(x(), radioGrp_1->y()+radioGrp_1->h(), w(), 70); // radio group for the mutex
- new geBox(x(), y()+80, 70, 25, "When the sequencer is halted:", FL_ALIGN_LEFT);
- chansStopOnSeqHalt_1 = new geRadio(x()+25, y()+105, 280, 20, "stop immediately all dynamic channels");
- chansStopOnSeqHalt_0 = new geRadio(x()+25, y()+130, 280, 20, "play all dynamic channels until finished");
- radioGrp_2->end();
-
- treatRecsAsLoops = new geCheck(x(), radioGrp_2->y()+radioGrp_2->h() + 15, 280, 20, "Treat one shot channels with actions as loops");
- inputMonitorDefaultOn = new geCheck(x(), treatRecsAsLoops->y()+treatRecsAsLoops->h() + 5, 280, 20, "New sample channels have input monitor on by default");
-
end();
+ label("Behaviors");
labelsize(G_GUI_FONT_SIZE_BASE);
selection_color(G_COLOR_GREY_4);
- m::conf::conf.recsStopOnChanHalt == 1 ? recsStopOnChanHalt_1->value(1) : recsStopOnChanHalt_0->value(1);
- m::conf::conf.chansStopOnSeqHalt == 1 ? chansStopOnSeqHalt_1->value(1) : chansStopOnSeqHalt_0->value(1);
- treatRecsAsLoops->value(m::conf::conf.treatRecsAsLoops);
- inputMonitorDefaultOn->value(m::conf::conf.inputMonitorDefaultOn);
-
- recsStopOnChanHalt_1->callback(cb_radio_mutex, (void*)this);
- recsStopOnChanHalt_0->callback(cb_radio_mutex, (void*)this);
- chansStopOnSeqHalt_1->callback(cb_radio_mutex, (void*)this);
- chansStopOnSeqHalt_0->callback(cb_radio_mutex, (void*)this);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geTabBehaviors::cb_radio_mutex(Fl_Widget* w, void* p)
-{
- static_cast<geTabBehaviors*>(p)->cb_radio_mutex(w);
-}
+ m_container.add(&m_chansStopOnSeqHalt);
+ m_container.add(&m_treatRecsAsLoops);
+ m_container.add(&m_inputMonitorDefaultOn);
+ m_container.add(&m_overdubProtectionDefaultOn);
+ add(m_container);
-/* -------------------------------------------------------------------------- */
-
-
-void geTabBehaviors::cb_radio_mutex(Fl_Widget* w)
-{
- static_cast<Fl_Button*>(w)->type(FL_RADIO_BUTTON);
+ m_chansStopOnSeqHalt.value(m::conf::conf.chansStopOnSeqHalt);
+ m_treatRecsAsLoops.value(m::conf::conf.treatRecsAsLoops);
+ m_inputMonitorDefaultOn.value(m::conf::conf.inputMonitorDefaultOn);
+ m_overdubProtectionDefaultOn.value(m::conf::conf.overdubProtectionDefaultOn);
}
void geTabBehaviors::save()
{
- m::conf::conf.recsStopOnChanHalt = recsStopOnChanHalt_1->value() == 1 ? 1 : 0;
- m::conf::conf.chansStopOnSeqHalt = chansStopOnSeqHalt_1->value() == 1 ? 1 : 0;
- m::conf::conf.treatRecsAsLoops = treatRecsAsLoops->value() == 1 ? 1 : 0;
- m::conf::conf.inputMonitorDefaultOn = inputMonitorDefaultOn->value() == 1 ? 1 : 0;
+ m::conf::conf.chansStopOnSeqHalt = m_chansStopOnSeqHalt.value();
+ m::conf::conf.treatRecsAsLoops = m_treatRecsAsLoops.value();
+ m::conf::conf.inputMonitorDefaultOn = m_inputMonitorDefaultOn.value();
+ m::conf::conf.overdubProtectionDefaultOn = m_overdubProtectionDefaultOn.value();
}
}} // giada::v::
\ No newline at end of file
#include <FL/Fl_Group.H>
-
-
-class geRadio;
-class geCheck;
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/check.h"
namespace giada {
void save();
- geRadio *recsStopOnChanHalt_1;
- geRadio *recsStopOnChanHalt_0;
- geRadio *chansStopOnSeqHalt_1;
- geRadio *chansStopOnSeqHalt_0;
- geCheck *treatRecsAsLoops;
- geCheck *inputMonitorDefaultOn;
-
-
private:
- static void cb_radio_mutex(Fl_Widget* w, void* p);
- void cb_radio_mutex(Fl_Widget* w);
+ gePack m_container;
+ geCheck m_chansStopOnSeqHalt;
+ geCheck m_treatRecsAsLoops;
+ geCheck m_inputMonitorDefaultOn;
+ geCheck m_overdubProtectionDefaultOn;
};
}} // giada::v::
* -------------------------------------------------------------------------- */
+#include <RtMidi.h>
#include <string>
#include "core/const.h"
-#ifdef G_OS_MAC
- #include <RtMidi.h>
-#else
- #include <rtmidi/RtMidi.h>
-#endif
#include "core/conf.h"
#include "core/midiMapConf.h"
#include "core/kernelMidi.h"
/* -------------------------------------------------------------------------- */
-void geTabMidi::cb_changeSystem(Fl_Widget* w, void* p) { ((geTabMidi*)p)->cb_changeSystem(); }
+void geTabMidi::cb_changeSystem(Fl_Widget* /*w*/, void* p) { ((geTabMidi*)p)->cb_changeSystem(); }
/* -------------------------------------------------------------------------- */
#include <FL/Fl_Group.H>
-class geChoice;
class geCheck;
namespace giada {
namespace v
{
+class geChoice;
class geTabMidi : public Fl_Group
{
public:
void fetchInPorts();
void fetchMidiMaps();
- static void cb_changeSystem(Fl_Widget* w, void* p);
+ static void cb_changeSystem(Fl_Widget* /*w*/, void* p);
void cb_changeSystem();
int systemInitValue;
#include <FL/Fl_Group.H>
-class geChoice;
-
-
namespace giada {
namespace v
{
+class geChoice;
class geTabMisc : public Fl_Group
{
public:
#include "core/const.h"
#include "core/conf.h"
#include "core/graphics.h"
-#include "core/pluginManager.h"
+#include "core/plugins/pluginManager.h"
#include "glue/plugin.h"
#include "utils/string.h"
#include "utils/fs.h"
#include "gui/dialogs/mainWindow.h"
#include "gui/dialogs/browser/browserDir.h"
#include "gui/elems/basics/box.h"
-#include "gui/elems/basics/radio.h"
#include "gui/elems/basics/check.h"
#include "gui/elems/basics/input.h"
#include "gui/elems/basics/button.h"
{
m_browse = new geButton(x()+w()-G_GUI_UNIT, y()+9, G_GUI_UNIT, G_GUI_UNIT, "", zoomInOff_xpm, zoomInOn_xpm);
m_folderPath = new geInput(m_browse->x()-258, y()+9, 250, G_GUI_UNIT);
- m_scanButton = new geButton(x()+w()-120, m_folderPath->y()+m_folderPath->h()+8, 120, G_GUI_UNIT);
- m_info = new geBox(x(), m_scanButton->y()+m_scanButton->h()+8, w(), 242);
+ m_scanButton = new geButton(x()+w()-150, m_folderPath->y()+m_folderPath->h()+8, 150, G_GUI_UNIT);
+ m_info = new geBox(x(), m_scanButton->y()+m_scanButton->h()+8, w(), 240);
end();
void geTabPlugins::refreshCount()
{
std::string scanLabel = "Scan (" + std::to_string(m::pluginManager::countAvailablePlugins()) + " found)";
- m_scanButton->label(scanLabel.c_str());
+ m_scanButton->copy_label(scanLabel.c_str());
}
/* -------------------------------------------------------------------------- */
-void geTabPlugins::cb_scan(Fl_Widget* w, void* p) { ((geTabPlugins*)p)->cb_scan(); }
-void geTabPlugins::cb_browse(Fl_Widget* w, void* p) { ((geTabPlugins*)p)->cb_browse(); }
+void geTabPlugins::cb_scan(Fl_Widget* /*w*/, void* p) { ((geTabPlugins*)p)->cb_scan(); }
+void geTabPlugins::cb_browse(Fl_Widget* /*w*/, void* p) { ((geTabPlugins*)p)->cb_browse(); }
/* -------------------------------------------------------------------------- */
private:
- static void cb_scan(Fl_Widget* w, void* p);
- static void cb_browse(Fl_Widget* w, void* p);
+ static void cb_scan(Fl_Widget* /*w*/, void* p);
+ static void cb_browse(Fl_Widget* /*w*/, void* p);
void cb_scan();
void cb_browse();
#include <FL/Fl.H>
#include <FL/fl_draw.H>
-#include "core/channels/channel.h"
#include "core/model/model.h"
#include "core/const.h"
#include "core/graphics.h"
-#include "core/pluginHost.h"
+#include "core/plugins/pluginHost.h"
#include "utils/gui.h"
#include "glue/channel.h"
+#include "glue/events.h"
#include "gui/dialogs/mainWindow.h"
#include "gui/dialogs/pluginList.h"
#include "gui/elems/basics/button.h"
namespace giada {
namespace v
{
-geChannel::geChannel(int X, int Y, int W, int H, ID channelId)
-: Fl_Group (X, Y, W, H),
- channelId(channelId)
+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::cb_arm(Fl_Widget* v, void* p) { ((geChannel*)p)->cb_arm(); }
-void geChannel::cb_mute(Fl_Widget* v, void* p) { ((geChannel*)p)->cb_mute(); }
-void geChannel::cb_solo(Fl_Widget* v, void* p) { ((geChannel*)p)->cb_solo(); }
-void geChannel::cb_changeVol(Fl_Widget* v, void* p) { ((geChannel*)p)->cb_changeVol(); }
+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* v, void* p) { ((geChannel*)p)->cb_openFxWindow(); }
+void geChannel::cb_openFxWindow(Fl_Widget* /*w*/, void* p) { ((geChannel*)p)->cb_openFxWindow(); }
#endif
void geChannel::refresh()
{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- if (mainButton->visible())
- mainButton->refresh();
- if (c.recStatus == ChannelStatus::WAIT || c.playStatus == ChannelStatus::WAIT)
- blink();
- playButton->setStatus(c.isPlaying());
- mute->setStatus(c.mute);
- solo->setStatus(c.solo);
- });
+ ChannelStatus playStatus = m_channel.a_getPlayStatus();
+ ChannelStatus recStatus = m_channel.a_getRecStatus();
+
+ if (mainButton->visible())
+ mainButton->refresh();
+
+ if (recStatus == ChannelStatus::WAIT || playStatus == ChannelStatus::WAIT)
+ blink();
+
+ playButton->setStatus(playStatus == ChannelStatus::PLAY || playStatus == ChannelStatus::ENDING);
+ mute->setStatus(m_channel.a_getMute());
+ solo->setStatus(m_channel.a_getSolo());
}
void geChannel::cb_arm()
{
- c::channel::setArm(channelId, arm->value());
+ c::events::toggleArmChannel(m_channel.id, Thread::MAIN);
}
void geChannel::cb_mute()
{
- c::channel::toggleMute(channelId);
+ c::events::toggleMuteChannel(m_channel.id, Thread::MAIN);
}
void geChannel::cb_solo()
{
- c::channel::toggleSolo(channelId);
+ c::events::toggleSoloChannel(m_channel.id, Thread::MAIN);
}
void geChannel::cb_changeVol()
{
- c::channel::setVolume(channelId, vol->value());
+ c::events::setChannelVolume(m_channel.id, vol->value(), Thread::MAIN);
}
#ifdef WITH_VST
void geChannel::cb_openFxWindow()
{
- u::gui::openSubWindow(G_MainWin, new v::gdPluginList(channelId), WID_FX_LIST);
+ u::gui::openSubWindow(G_MainWin, new v::gdPluginList(m_channel.id), WID_FX_LIST);
}
#endif
bool geChannel::handleKey(int e)
{
- m::model::ChannelsLock l(m::model::channels);
- const m::Channel& ch = m::model::get(m::model::channels, channelId);
-
- if (Fl::event_key() != ch.key)
+ if (Fl::event_key() != m_channel.key)
return false;
if (e == FL_KEYDOWN && !playButton->value()) { // Key not already pressed
playButton->value(0);
return true;
}
-
+
return false;
}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+const c::channel::Data& geChannel::getData() const
+{
+ return m_channel;
+}
}} // giada::v::
#include <FL/Fl_Group.H>
+#include "glue/channel.h"
#include "core/types.h"
namespace giada {
-namespace m
-{
-class Channel;
-}
namespace v
{
class geChannelButton;
-
class geChannel : public Fl_Group
{
public:
- geChannel(int x, int y, int w, int h, ID channelId);
+ geChannel(int x, int y, int w, int h, c::channel::Data d);
void draw() override;
bool handleKey(int e);
- ID channelId;
+ /* getData
+ Returns a reference to the internal data. Read-only. */
+
+ const c::channel::Data& getData() const;
geStatusButton* playButton;
geButton* arm;
static const int MIN_ELEM_W = 20;
- static void cb_arm(Fl_Widget* v, void* p);
- static void cb_mute(Fl_Widget* v, void* p);
- static void cb_solo(Fl_Widget* v, void* p);
- static void cb_changeVol(Fl_Widget* v, void* p);
+ 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* v, void* p);
+ 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();
+ void cb_openFxWindow();
#endif
/* blink
Spread widgets across available space. */
void packWidgets();
+
+ /* m_channel
+ Channel's data. */
+
+ c::channel::Data m_channel;
};
}} // giada::v::
#include "core/model/model.h"
#include "core/const.h"
#include "core/recorder.h"
+#include "glue/channel.h"
#include "utils/string.h"
#include "channelButton.h"
namespace giada {
namespace v
{
-geChannelButton::geChannelButton(int x, int y, int w, int h, ID channelId)
-: geButton (x, y, w, h),
- m_channelId(channelId),
- m_key ("")
+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()
{
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- switch (c.playStatus) {
- case ChannelStatus::OFF:
- case ChannelStatus::EMPTY:
- setDefaultMode(); break;
- case ChannelStatus::PLAY:
- setPlayMode(); break;
- case ChannelStatus::ENDING:
- setEndingMode(); break;
- default: break;
- }
-
- switch (c.recStatus) {
- case ChannelStatus::ENDING:
- setEndingMode(); break;
- default: break;
- }
- });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geChannelButton::setKey(int k)
-{
- m_key = k == 0 ? "" : std::string(1, k);
- redraw();
+ switch (m_channel.a_getPlayStatus()) {
+ case ChannelStatus::OFF:
+ case ChannelStatus::EMPTY:
+ setDefaultMode(); break;
+ case ChannelStatus::PLAY:
+ setPlayMode(); break;
+ case ChannelStatus::ENDING:
+ setEndingMode(); break;
+ default: break;
+ }
+ switch (m_channel.a_getRecStatus()) {
+ case ChannelStatus::ENDING:
+ setEndingMode(); break;
+ default: break;
+ }
}
{
geButton::draw();
- if (m_key == "")
+ if (m_channel.key == 0)
return;
/* draw background */
fl_color(G_COLOR_LIGHT_2);
fl_font(FL_HELVETICA, 11);
- fl_draw(m_key.c_str(), x(), y(), 18, h(), FL_ALIGN_CENTER);
+ fl_draw(std::string(1, static_cast<wchar_t>(m_channel.key)).c_str(), x(), y(), 18, h(), FL_ALIGN_CENTER);
}
{
bgColor0 = G_COLOR_GREY_4;
}
-
}} // giada::v::
namespace giada {
-namespace m
-{
-class Channel;
-}
+namespace c {
+namespace channel
+{
+struct Data;
+}}
namespace v
{
class geChannelButton : public geButton
{
public:
- geChannelButton(int x, int y, int w, int h, ID channelId);
+ geChannelButton(int x, int y, int w, int h, const c::channel::Data& d);
virtual void refresh();
void draw() override;
- void setKey(int k);
void setPlayMode();
void setEndingMode();
void setDefaultMode(const char* l=0);
protected:
- ID m_channelId;
- std::string m_key;
+ const c::channel::Data& m_channel;
};
}} // giada::v::
#include <cassert>
#include <FL/fl_draw.H>
#include "utils/gui.h"
-#include "core/channels/sampleChannel.h"
+#include "core/channels/channel.h"
+#include "core/channels/samplePlayer.h"
#include "core/model/model.h"
#include "core/graphics.h"
#include "core/const.h"
namespace giada {
namespace v
{
-geChannelMode::geChannelMode(int x, int y, int w, int h, ID channelId)
-: Fl_Menu_Button(x, y, w, h),
- m_channelId (channelId)
+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*) ChannelMode::LOOP_BASIC);
- add("Loop . once", 0, cb_changeMode, (void*) ChannelMode::LOOP_ONCE);
- add("Loop . once . bar", 0, cb_changeMode, (void*) ChannelMode::LOOP_ONCE_BAR);
- add("Loop . repeat", 0, cb_changeMode, (void*) ChannelMode::LOOP_REPEAT);
- add("Oneshot . basic", 0, cb_changeMode, (void*) ChannelMode::SINGLE_BASIC);
- add("Oneshot . press", 0, cb_changeMode, (void*) ChannelMode::SINGLE_PRESS);
- add("Oneshot . retrig", 0, cb_changeMode, (void*) ChannelMode::SINGLE_RETRIG);
- add("Oneshot . endless", 0, cb_changeMode, (void*) ChannelMode::SINGLE_ENDLESS);
+ add("Loop . basic", 0, cb_changeMode, (void*) SamplePlayerMode::LOOP_BASIC);
+ add("Loop . once", 0, cb_changeMode, (void*) SamplePlayerMode::LOOP_ONCE);
+ add("Loop . once . bar", 0, cb_changeMode, (void*) SamplePlayerMode::LOOP_ONCE_BAR);
+ add("Loop . repeat", 0, cb_changeMode, (void*) SamplePlayerMode::LOOP_REPEAT);
+ add("Oneshot . basic", 0, cb_changeMode, (void*) SamplePlayerMode::SINGLE_BASIC);
+ add("Oneshot . press", 0, cb_changeMode, (void*) SamplePlayerMode::SINGLE_PRESS);
+ add("Oneshot . retrig", 0, cb_changeMode, (void*) SamplePlayerMode::SINGLE_RETRIG);
+ add("Oneshot . endless", 0, cb_changeMode, (void*) SamplePlayerMode::SINGLE_ENDLESS);
+
+ value(static_cast<int>(m_channel.sample->mode));
}
{
fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4); // border
- m::model::ChannelsLock l(m::model::channels);
- const m::SampleChannel& ch = static_cast<m::SampleChannel&>(m::model::get(m::model::channels, m_channelId));
-
- switch (ch.mode) {
- case ChannelMode::LOOP_BASIC:
+ switch (m_channel.sample->mode) {
+ case SamplePlayerMode::LOOP_BASIC:
fl_draw_pixmap(loopBasic_xpm, x()+1, y()+1);
break;
- case ChannelMode::LOOP_ONCE:
+ case SamplePlayerMode::LOOP_ONCE:
fl_draw_pixmap(loopOnce_xpm, x()+1, y()+1);
break;
- case ChannelMode::LOOP_ONCE_BAR:
+ case SamplePlayerMode::LOOP_ONCE_BAR:
fl_draw_pixmap(loopOnceBar_xpm, x()+1, y()+1);
break;
- case ChannelMode::LOOP_REPEAT:
+ case SamplePlayerMode::LOOP_REPEAT:
fl_draw_pixmap(loopRepeat_xpm, x()+1, y()+1);
break;
- case ChannelMode::SINGLE_BASIC:
+ case SamplePlayerMode::SINGLE_BASIC:
fl_draw_pixmap(oneshotBasic_xpm, x()+1, y()+1);
break;
- case ChannelMode::SINGLE_PRESS:
+ case SamplePlayerMode::SINGLE_PRESS:
fl_draw_pixmap(oneshotPress_xpm, x()+1, y()+1);
break;
- case ChannelMode::SINGLE_RETRIG:
+ case SamplePlayerMode::SINGLE_RETRIG:
fl_draw_pixmap(oneshotRetrig_xpm, x()+1, y()+1);
break;
- case ChannelMode::SINGLE_ENDLESS:
+ case SamplePlayerMode::SINGLE_ENDLESS:
fl_draw_pixmap(oneshotEndless_xpm, x()+1, y()+1);
break;
}
/* -------------------------------------------------------------------------- */
-void geChannelMode::cb_changeMode(Fl_Widget* v, void* p) { ((geChannelMode*)v)->cb_changeMode((intptr_t)p); }
+void geChannelMode::cb_changeMode(Fl_Widget* w, void* p) { ((geChannelMode*)w)->cb_changeMode((intptr_t)p); }
/* -------------------------------------------------------------------------- */
void geChannelMode::cb_changeMode(int mode)
{
- c::channel::setSampleMode(m_channelId, static_cast<ChannelMode>(mode));
+ c::channel::setSamplePlayerMode(m_channel.id, static_cast<SamplePlayerMode>(mode));
}
}} // giada::v::
namespace giada {
-namespace m
-{
-class SampleChannel;
-}
namespace v
{
class geChannelMode : public Fl_Menu_Button
{
public:
- geChannelMode(int x, int y, int w, int h, ID channelId);
+ geChannelMode(int x, int y, int w, int h, c::channel::Data& d);
void draw() override;
private:
- static void cb_changeMode(Fl_Widget* v, void* p);
+ static void cb_changeMode(Fl_Widget* /*w*/, void* p);
void cb_changeMode(int mode);
- ID m_channelId;
+ c::channel::Data& m_channel;
};
}} // giada::v::
#include <FL/fl_draw.H>
-#include "core/channels/sampleChannel.h"
-#include "core/model/model.h"
-#include "core/mixer.h"
-#include "core/clock.h"
-#include "core/recorder.h"
-#include "core/recManager.h"
#include "core/const.h"
+#include "glue/channel.h"
#include "channelStatus.h"
namespace giada {
namespace v
{
-geChannelStatus::geChannelStatus(int x, int y, int w, int h, ID channelId)
-: Fl_Box(x, y, w, h), channelId(channelId)
+geChannelStatus::geChannelStatus(int x, int y, int w, int h, c::channel::Data& d)
+: Fl_Box (x, y, w, h)
+, m_channel(d)
{
}
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
- m::model::ChannelsLock l(m::model::channels);
- const m::SampleChannel& ch = static_cast<m::SampleChannel&>(m::model::get(m::model::channels, channelId));
-
- if (ch.playStatus == ChannelStatus::WAIT ||
- ch.playStatus == ChannelStatus::ENDING ||
- ch.recStatus == ChannelStatus::WAIT ||
- ch.recStatus == ChannelStatus::ENDING)
+ ChannelStatus playStatus = m_channel.a_getPlayStatus();
+ ChannelStatus recStatus = m_channel.a_getRecStatus();
+ Pixel pos = 0;
+
+ 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 (ch.playStatus == ChannelStatus::PLAY)
+ if (playStatus == ChannelStatus::PLAY) {
+ /* Equation for the progress bar:
+ ((chanTracker - chanStart) * w()) / (chanEnd - chanStart). */
+ Frame tracker = m_channel.sample->a_getTracker();
+ Frame begin = m_channel.sample->a_getBegin();
+ Frame end = m_channel.sample->a_getEnd();
+ pos = ((tracker - begin) * (w() - 1)) / ((end - begin));
fl_rect(x(), y(), w(), h(), G_COLOR_LIGHT_1);
+ }
else
fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2); // status empty
-
- if (m::recManager::isRecordingInput() && ch.armed)
- fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_RED); // take in progress
- else
- if (m::recManager::isRecordingAction())
- fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_BLUE); // action recording
-
- /* Equation for the progress bar:
- ((chanTracker - chanStart) * w()) / (chanEnd - chanStart). */
-
- int pos = ch.getPosition();
- if (pos == -1)
- pos = 0;
- else
- pos = (pos * (w()-1)) / ((ch.getEnd() - ch.getBegin()));
- fl_rectf(x()+1, y()+1, pos, h()-2, G_COLOR_LIGHT_1);
+ if (pos != 0)
+ fl_rectf(x()+1, y()+1, pos, h()-2, G_COLOR_LIGHT_1);
}
}} // giada::v::
namespace giada {
-namespace m
-{
-class SampleChannel;
-}
+namespace c {
+namespace channel
+{
+struct Data;
+}}
namespace v
{
class geChannelStatus : public Fl_Box
{
public:
- geChannelStatus(int x, int y, int w, int h, ID channelId);
+ geChannelStatus(int x, int y, int w, int h, c::channel::Data& d);
void draw() override;
- ID channelId;
+private:
+
+ c::channel::Data& m_channel;
};
}} // giada::v::
#include <cassert>
#include <FL/fl_draw.H>
#include <FL/Fl_Menu_Button.H>
-#include "core/channels/sampleChannel.h"
-#include "core/channels/midiChannel.h"
+#include "core/channels/state.h"
#include "core/model/model.h"
#include "glue/channel.h"
#include "utils/log.h"
/* -------------------------------------------------------------------------- */
-void geColumn::cb_addChannel(Fl_Widget* v, void* p) { ((geColumn*)p)->cb_addChannel(); }
+void geColumn::cb_addChannel(Fl_Widget* /*w*/, void* p) { ((geColumn*)p)->cb_addChannel(); }
/* -------------------------------------------------------------------------- */
-geChannel* geColumn::addChannel(ID channelId, ChannelType t, int height)
+geChannel* geColumn::addChannel(c::channel::Data d)
{
geChannel* gch = nullptr;
Fl_Widget* last = m_channels.size() == 0 ? static_cast<Fl_Widget*>(m_addChannelBtn) : m_channels.back();
- if (t == ChannelType::SAMPLE)
- gch = new geSampleChannel(x(), last->y() + last->h() + G_GUI_INNER_MARGIN, w(), height, channelId);
+ 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(), height, channelId);
+ gch = new geMidiChannel (x(), last->y() + last->h() + G_GUI_INNER_MARGIN, w(), d.height, d);
geResizerBar* bar = new geResizerBar(x(), gch->y() + gch->h(), w(),
G_GUI_INNER_MARGIN, G_GUI_UNIT, geResizerBar::VERTICAL, gch);
/* Update the column height while dragging the resizer bar. */
- bar->onDrag = [=](const Fl_Widget* w)
+ 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 = [=](const Fl_Widget* w)
+ bar->onRelease = [channelId = d.id, this](const Fl_Widget* w)
{
- storeChannelHeight(w, channelId);
+ resizable(this);
+ c::channel::setHeight(channelId, w->h());
};
m_channels.push_back(gch);
geChannel* geColumn::getChannel(ID channelId) const
{
for (geChannel* c : m_channels)
- if (c->channelId == channelId)
+ if (c->getData().id == channelId)
return c;
return nullptr;
}
out += c->h() + G_GUI_INNER_MARGIN;
return out + m_addChannelBtn->h() + G_GUI_INNER_MARGIN;
}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geColumn::storeChannelHeight(const Fl_Widget* w, ID channelId) const
-{
- m::model::onSwap(m::model::channels, channelId, [&](m::Channel& c)
- {
- c.height = w->h();
- });
-}
-
}} // giada::v::
#include <functional>
#include <vector>
#include <FL/Fl_Group.H>
+#include "glue/channel.h"
#include "core/types.h"
/* addChannel
Adds a new channel in this column. */
- geChannel* addChannel(ID channelId, ChannelType t, int size);
+ geChannel* addChannel(c::channel::Data d);
/* refreshChannels
Updates channels' graphical statues. Called on each GUI cycle. */
private:
- static void cb_addChannel(Fl_Widget* v, void* p);
+ static void cb_addChannel(Fl_Widget* /*w*/, void* p);
void cb_addChannel();
int countChannels() const;
int computeHeight() const;
- void storeChannelHeight(const Fl_Widget* c, ID channelId) const;
std::vector<geChannel*> m_channels;
#include <cassert>
#include <FL/fl_draw.H>
-#include "core/model/model.h"
-#include "core/channels/sampleChannel.h"
#include "glue/io.h"
#include "glue/channel.h"
#include "utils/fs.h"
namespace v
{
geKeyboard::geKeyboard(int X, int Y, int W, int H)
-: Fl_Scroll (X, Y, W, H),
- m_addColumnBtn(nullptr)
+: geScroll (X, Y, W, H, Fl_Scroll::BOTH_ALWAYS)
+, m_addColumnBtn(nullptr)
{
end();
-
- color(G_COLOR_GREY_1);
- type(Fl_Scroll::BOTH_ALWAYS);
- 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);
-
init();
}
for (ColumnLayout c : layout)
addColumn(c.width, c.id);
- /* Parse the model and assign each channel to its column. */
-
- m::model::ChannelsLock lock(m::model::channels);
-
- for (const m::Channel* ch : m::model::channels)
- if (!ch->isInternal())
- getColumn(ch->columnId)->addChannel(ch->id, ch->type, ch->height);
+ for (const c::channel::Data& ch : c::channel::getChannels())
+ getColumn(ch.columnId)->addChannel(ch);
redraw();
}
/* -------------------------------------------------------------------------- */
-void geKeyboard::cb_addColumn(Fl_Widget* v, void* p)
+void geKeyboard::cb_addColumn(Fl_Widget* /*w*/, void* p)
{
((geKeyboard*)p)->cb_addColumn();
}
/* Store the column width in layout when the resizer bar is released. */
- bar->onRelease = [=](const Fl_Widget* w)
+ bar->onRelease = [=](const Fl_Widget* /*w*/)
{
storeLayout();
};
#include <vector>
-#include <FL/Fl_Scroll.H>
-#include "core/channels/channel.h"
+#include <functional>
+#include "gui/elems/basics/scroll.h"
#include "core/idManager.h"
#include "core/const.h"
{
class geColumn;
class geChannel;
-class geSampleChannel;
-
-class geKeyboard : public Fl_Scroll
+class geKeyboard : public geScroll
{
public:
static constexpr int COLUMN_GAP = 20;
- static void cb_addColumn(Fl_Widget* v, void* p);
+ static void cb_addColumn(Fl_Widget* /*w*/, void* p);
void cb_addColumn();
void addColumn(int width=G_DEFAULT_COLUMN_WIDTH, ID id=0);
#include <FL/Fl_Menu_Button.H>
#include "core/const.h"
#include "core/graphics.h"
-#include "core/channels/midiChannel.h"
#include "core/model/model.h"
#include "core/recorder.h"
#include "utils/gui.h"
void menuCallback(Fl_Widget* w, void* v)
{
- geMidiChannel* gch = static_cast<geMidiChannel*>(w);
+ const geMidiChannel* gch = static_cast<geMidiChannel*>(w);
+ const c::channel::Data& data = gch->getData();
- Menu selectedItem = (Menu) (intptr_t) v;
-
- switch (selectedItem)
+ switch ((Menu) (intptr_t) v)
{
case Menu::CLEAR_ACTIONS:
case Menu::__END_CLEAR_ACTION_SUBMENU__:
break;
case Menu::EDIT_ACTIONS:
- u::gui::openSubWindow(G_MainWin, new v::gdMidiActionEditor(gch->channelId), WID_ACTION_EDITOR);
+ u::gui::openSubWindow(G_MainWin, new v::gdMidiActionEditor(data.id), WID_ACTION_EDITOR);
break;
case Menu::CLEAR_ACTIONS_ALL:
- c::recorder::clearAllActions(gch->channelId);
+ c::recorder::clearAllActions(data.id);
break;
case Menu::SETUP_KEYBOARD_INPUT:
- u::gui::openSubWindow(G_MainWin, new gdKeyGrabber(gch->channelId), WID_KEY_GRABBER);
+ u::gui::openSubWindow(G_MainWin, new gdKeyGrabber(data), WID_KEY_GRABBER);
break;
case Menu::SETUP_MIDI_INPUT:
- u::gui::openSubWindow(G_MainWin, new gdMidiInputChannel(gch->channelId), WID_MIDI_INPUT);
+ u::gui::openSubWindow(G_MainWin, new gdMidiInputChannel(data.id), WID_MIDI_INPUT);
break;
case Menu::SETUP_MIDI_OUTPUT:
- u::gui::openSubWindow(G_MainWin, new gdMidiOutputMidiCh(gch->channelId), WID_MIDI_OUTPUT);
+ u::gui::openSubWindow(G_MainWin, new gdMidiOutputMidiCh(data.id), WID_MIDI_OUTPUT);
break;
case Menu::CLONE_CHANNEL:
- c::channel::cloneChannel(gch->channelId);
+ c::channel::cloneChannel(data.id);
break;
case Menu::RENAME_CHANNEL:
- u::gui::openSubWindow(G_MainWin, new gdChannelNameInput(gch->channelId), WID_SAMPLE_NAME);
+ u::gui::openSubWindow(G_MainWin, new gdChannelNameInput(data), WID_SAMPLE_NAME);
break;
case Menu::DELETE_CHANNEL:
- c::channel::deleteChannel(gch->channelId);
+ c::channel::deleteChannel(data.id);
break;
}
}
/* -------------------------------------------------------------------------- */
-geMidiChannel::geMidiChannel(int X, int Y, int W, int H, ID channelId)
-: geChannel(X, Y, W, H, channelId)
+geMidiChannel::geMidiChannel(int X, int Y, int W, int H, c::channel::Data d)
+: geChannel(X, Y, W, H, d)
+, m_data (d)
{
#if defined(WITH_VST)
constexpr int delta = 6 * (G_GUI_UNIT + G_GUI_INNER_MARGIN);
playButton = new geStatusButton (x(), y(), G_GUI_UNIT, G_GUI_UNIT, channelStop_xpm, channelPlay_xpm);
arm = new geButton (playButton->x() + playButton->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, "", armOff_xpm, armOn_xpm);
- mainButton = new geMidiChannelButton(arm->x() + arm->w() + G_GUI_INNER_MARGIN, y(), w() - delta, H, channelId);
+ mainButton = new geMidiChannelButton(arm->x() + arm->w() + G_GUI_INNER_MARGIN, y(), w() - delta, H, m_channel);
mute = new geStatusButton (mainButton->x() + mainButton->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, muteOff_xpm, muteOn_xpm);
solo = new geStatusButton (mute->x() + mute->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, soloOff_xpm, soloOn_xpm);
#if defined(WITH_VST)
resizable(mainButton);
- m::model::ChannelsLock l(m::model::channels);
- const m::Channel& ch = m::model::get(m::model::channels, channelId);
-
#ifdef WITH_VST
- fx->setStatus(ch.pluginIds.size() > 0);
+ fx->setStatus(m_channel.pluginIds.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(ch.armed);
+ arm->value(m_channel.a_isArmed());
arm->callback(cb_arm, (void*)this);
#ifdef WITH_VST
solo->type(FL_TOGGLE_BUTTON);
solo->callback(cb_solo, (void*)this);
- mainButton->setKey(ch.key);
mainButton->callback(cb_openMenu, (void*)this);
- vol->value(ch.volume);
+ vol->value(m_channel.volume);
vol->callback(cb_changeVol, (void*)this);
size(w(), h()); // Force responsiveness
/* -------------------------------------------------------------------------- */
-void geMidiChannel::cb_playButton(Fl_Widget* v, void* p) { ((geMidiChannel*)p)->cb_playButton(); }
-void geMidiChannel::cb_openMenu(Fl_Widget* v, void* p) { ((geMidiChannel*)p)->cb_openMenu(); }
+void geMidiChannel::cb_playButton(Fl_Widget* /*w*/, void* p) { ((geMidiChannel*)p)->cb_playButton(); }
+void geMidiChannel::cb_openMenu(Fl_Widget* /*w*/, void* p) { ((geMidiChannel*)p)->cb_openMenu(); }
/* -------------------------------------------------------------------------- */
void geMidiChannel::cb_playButton()
{
- v::dispatcher::dispatchTouch(this, playButton->value());
+ v::dispatcher::dispatchTouch(*this, playButton->value());
}
Fl_Menu_Item rclick_menu[] = {
{"Edit actions...", 0, menuCallback, (void*) Menu::EDIT_ACTIONS},
{"Clear actions", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS, FL_SUBMENU},
- {"All", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_ALL},
+ {"All", 0, menuCallback, (void*) Menu::CLEAR_ACTIONS_ALL},
{0},
{"Setup keyboard input...", 0, menuCallback, (void*) Menu::SETUP_KEYBOARD_INPUT},
{"Setup MIDI input...", 0, menuCallback, (void*) Menu::SETUP_MIDI_INPUT},
/* No 'clear actions' if there are no actions. */
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- if (!c.hasActions)
- rclick_menu[(int)Menu::CLEAR_ACTIONS].deactivate();
- });
+ 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);
namespace giada {
-namespace m
-{
-class MidiChannel;
-}
namespace v
{
class geMidiChannel : public geChannel
{
public:
- geMidiChannel(int x, int y, int w, int h, ID channelId);
+ 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* v, void* p);
- static void cb_openMenu(Fl_Widget* v, void* p);
+ 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;
};
}} // giada::v::
#include "utils/string.h"
-#include "core/channels/midiChannel.h"
-#include "core/model/model.h"
-#include "core/recManager.h"
+#include "glue/channel.h"
#include "midiChannelButton.h"
namespace giada {
namespace v
{
-geMidiChannelButton::geMidiChannelButton(int x, int y, int w, int h, ID channelId)
-: geChannelButton(x, y, w, h, channelId)
+geMidiChannelButton::geMidiChannelButton(int x, int y, int w, int h, const c::channel::Data& d)
+: geChannelButton(x, y, w, h, d)
{
- std::string l;
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- const m::MidiChannel& mc = static_cast<m::MidiChannel&>(c);
- if (mc.name.empty())
- l = "-- MIDI --";
- else
- l = mc.name.c_str();
-
- if (mc.midiOut)
- l += " (ch " + u::string::iToString(mc.midiOutChan + 1) + " out)";
- });
-
- label(l.c_str());
}
{
geChannelButton::refresh();
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- if (m::recManager::isRecordingAction() && c.armed)
- setActionRecordMode();
- });
+ refreshLabel();
+
+ if (m_channel.a_isRecordingAction() && m_channel.a_isArmed())
+ setActionRecordMode();
redraw();
}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geMidiChannelButton::refreshLabel()
+{
+ std::string l = m_channel.name.empty() ? "-- MIDI --" : m_channel.name;
+
+ if (m_channel.midi->a_isOutputEnabled())
+ l += " (ch " + std::to_string(m_channel.midi->a_getFilter() + 1) + " out)";
+
+ copy_label(l.c_str());
+}
}} // giada::v::
namespace giada {
-namespace m
-{
-class MidiChannel;
-}
namespace v
{
class geMidiChannelButton : public geChannelButton
{
public:
- geMidiChannelButton(int x, int y, int w, int h, ID channelId);
+ geMidiChannelButton(int x, int y, int w, int h, const c::channel::Data& d);
void refresh() override;
+
+private:
+
+ void refreshLabel();
};
}} // giada::v::
#include <cassert>
-#include "core/channels/sampleChannel.h"
+#include "core/channels/channel.h"
+#include "core/channels/samplePlayer.h"
#include "core/model/model.h"
#include "core/mixer.h"
#include "core/conf.h"
#include "core/recManager.h"
#include "glue/io.h"
#include "glue/channel.h"
+#include "glue/events.h"
#include "glue/recorder.h"
#include "glue/storage.h"
#include "utils/gui.h"
enum class Menu
{
INPUT_MONITOR = 0,
+ OVERDUB_PROTECTION,
LOAD_SAMPLE,
EXPORT_SAMPLE,
SETUP_KEYBOARD_INPUT,
void menuCallback(Fl_Widget* w, void* v)
{
- geSampleChannel* gch = static_cast<geSampleChannel*>(w);
+ const geSampleChannel* gch = static_cast<geSampleChannel*>(w);
+ const c::channel::Data& data = gch->getData();
- ID waveId;
- bool inputMonitor;
- m::model::onGet(m::model::channels, gch->channelId, [&](m::Channel& c)
- {
- waveId = static_cast<m::SampleChannel&>(c).waveId;
- inputMonitor = static_cast<m::SampleChannel&>(c).inputMonitor;
- });
-
- Menu selectedItem = (Menu) (intptr_t) v;
-
- switch (selectedItem) {
+ switch ((Menu) (intptr_t) v) {
case Menu::INPUT_MONITOR: {
- c::channel::setInputMonitor(gch->channelId, !inputMonitor);
+ c::channel::setInputMonitor(data.id, !data.sample->a_getInputMonitor());
+ break;
+ }
+ case Menu::OVERDUB_PROTECTION: {
+ c::channel::setOverdubProtection(data.id, !data.sample->a_getOverdubProtection());
break;
}
case Menu::LOAD_SAMPLE: {
gdWindow* w = new gdBrowserLoad("Browse sample",
- m::conf::conf.samplePath.c_str(), c::storage::loadSample, gch->channelId);
+ m::conf::conf.samplePath.c_str(), c::storage::loadSample, data.id);
u::gui::openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
break;
}
case Menu::EXPORT_SAMPLE: {
gdWindow* w = new gdBrowserSave("Save sample",
- m::conf::conf.samplePath.c_str(), "", c::storage::saveSample, gch->channelId);
+ m::conf::conf.samplePath.c_str(), "", c::storage::saveSample, data.id);
u::gui::openSubWindow(G_MainWin, w, WID_FILE_BROWSER);
break;
}
case Menu::SETUP_KEYBOARD_INPUT: {
- u::gui::openSubWindow(G_MainWin, new gdKeyGrabber(gch->channelId),
+ u::gui::openSubWindow(G_MainWin, new gdKeyGrabber(data),
WID_KEY_GRABBER);
break;
}
case Menu::SETUP_MIDI_INPUT: {
- u::gui::openSubWindow(G_MainWin, new gdMidiInputChannel(gch->channelId),
+ u::gui::openSubWindow(G_MainWin, new gdMidiInputChannel(data.id),
WID_MIDI_INPUT);
break;
}
case Menu::SETUP_MIDI_OUTPUT: {
- u::gui::openSubWindow(G_MainWin, new gdMidiOutputSampleCh(gch->channelId),
+ u::gui::openSubWindow(G_MainWin, new gdMidiOutputSampleCh(data.id),
WID_MIDI_OUTPUT);
break;
}
case Menu::EDIT_SAMPLE: {
- u::gui::openSubWindow(G_MainWin, new gdSampleEditor(gch->channelId, waveId),
+ u::gui::openSubWindow(G_MainWin, new gdSampleEditor(data.id),
WID_SAMPLE_EDITOR);
break;
}
case Menu::EDIT_ACTIONS: {
- u::gui::openSubWindow(G_MainWin, new gdSampleActionEditor(gch->channelId),
+ u::gui::openSubWindow(G_MainWin, new gdSampleActionEditor(data.id),
WID_ACTION_EDITOR);
break;
}
case Menu::__END_CLEAR_ACTIONS_SUBMENU__:
break;
case Menu::CLEAR_ACTIONS_ALL: {
- c::recorder::clearAllActions(gch->channelId);
+ c::recorder::clearAllActions(data.id);
break;
}
case Menu::CLEAR_ACTIONS_VOLUME: {
- c::recorder::clearVolumeActions(gch->channelId);
+ c::recorder::clearVolumeActions(data.id);
break;
}
case Menu::CLEAR_ACTIONS_START_STOP: {
- c::recorder::clearStartStopActions(gch->channelId);
+ c::recorder::clearStartStopActions(data.id);
break;
}
case Menu::CLONE_CHANNEL: {
- c::channel::cloneChannel(gch->channelId);
+ c::channel::cloneChannel(data.id);
break;
}
case Menu::RENAME_CHANNEL: {
- u::gui::openSubWindow(G_MainWin, new gdChannelNameInput(gch->channelId),
+ u::gui::openSubWindow(G_MainWin, new gdChannelNameInput(data),
WID_SAMPLE_NAME);
break;
}
case Menu::FREE_CHANNEL: {
- c::channel::freeChannel(gch->channelId);
+ c::channel::freeChannel(data.id);
break;
}
case Menu::DELETE_CHANNEL: {
- c::channel::deleteChannel(gch->channelId);
+ c::channel::deleteChannel(data.id);
break;
}
}
/* -------------------------------------------------------------------------- */
-geSampleChannel::geSampleChannel(int X, int Y, int W, int H, ID channelId)
-: geChannel(X, Y, W, H, channelId)
+geSampleChannel::geSampleChannel(int X, int Y, int W, int H, c::channel::Data d)
+: geChannel(X, Y, W, H, d)
{
#if defined(WITH_VST)
constexpr int delta = 9 * (G_GUI_UNIT + G_GUI_INNER_MARGIN);
#endif
playButton = new geStatusButton (x(), y(), G_GUI_UNIT, G_GUI_UNIT, channelStop_xpm, channelPlay_xpm);
- arm = new geButton (playButton->x() + playButton->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, "", armOff_xpm, armOn_xpm);
- status = new geChannelStatus (arm->x() + arm->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, H, channelId);
- mainButton = new geSampleChannelButton(status->x() + status->w() + G_GUI_INNER_MARGIN, y(), w() - delta, H, channelId);
+ arm = new geButton (playButton->x() + playButton->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, "", armOff_xpm, armOn_xpm, armDisabled_xpm);
+ status = new geChannelStatus (arm->x() + arm->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, H, m_channel);
+ mainButton = new geSampleChannelButton(status->x() + status->w() + G_GUI_INNER_MARGIN, y(), w() - delta, H, m_channel);
readActions = new geStatusButton (mainButton->x() + mainButton->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, readActionOff_xpm, readActionOn_xpm, readActionDisabled_xpm);
- modeBox = new geChannelMode (readActions->x() + readActions->w() + G_GUI_INNER_MARGIN, y(), G_GUI_UNIT, G_GUI_UNIT, channelId);
+ 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)
resizable(mainButton);
- m::model::ChannelsLock l(m::model::channels);
- const m::SampleChannel& ch = static_cast<m::SampleChannel&>(m::model::get(m::model::channels, channelId));
-
- modeBox->value(static_cast<int>(ch.mode));
- modeBox->redraw();
-
#ifdef WITH_VST
- fx->setStatus(ch.pluginIds.size() > 0);
+ fx->setStatus(m_channel.pluginIds.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(ch.armed);
+ arm->value(m_channel.a_isArmed());
arm->callback(cb_arm, (void*)this);
#ifdef WITH_VST
solo->type(FL_TOGGLE_BUTTON);
solo->callback(cb_solo, (void*)this);
- mainButton->setKey(ch.key);
mainButton->callback(cb_openMenu, (void*)this);
- readActions->setStatus(ch.readActions);
readActions->callback(cb_readActions, (void*)this);
- vol->value(ch.volume);
+ vol->value(m_channel.volume);
vol->callback(cb_changeVol, (void*)this);
size(w(), h()); // Force responsiveness
/* -------------------------------------------------------------------------- */
-void geSampleChannel::cb_playButton (Fl_Widget* v, void* p) { ((geSampleChannel*)p)->cb_playButton(); }
-void geSampleChannel::cb_openMenu (Fl_Widget* v, void* p) { ((geSampleChannel*)p)->cb_openMenu(); }
-void geSampleChannel::cb_readActions(Fl_Widget* v, void* p) { ((geSampleChannel*)p)->cb_readActions(); }
+void geSampleChannel::cb_playButton (Fl_Widget* /*w*/, void* p) { ((geSampleChannel*)p)->cb_playButton(); }
+void geSampleChannel::cb_openMenu (Fl_Widget* /*w*/, void* p) { ((geSampleChannel*)p)->cb_openMenu(); }
+void geSampleChannel::cb_readActions(Fl_Widget* /*w*/, void* p) { ((geSampleChannel*)p)->cb_readActions(); }
/* -------------------------------------------------------------------------- */
void geSampleChannel::cb_playButton()
{
- v::dispatcher::dispatchTouch(this, playButton->value());
+ v::dispatcher::dispatchTouch(*this, playButton->value());
}
void geSampleChannel::cb_openMenu()
{
- bool inputMonitor;
- bool isEmptyOrMissing;
- bool hasActions;
- bool isAnyLoopMode;
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- const m::SampleChannel& sc = static_cast<m::SampleChannel&>(c);
- inputMonitor = sc.inputMonitor;
- isEmptyOrMissing = sc.playStatus == ChannelStatus::EMPTY || sc.playStatus == ChannelStatus::MISSING;
- hasActions = sc.hasActions;
- isAnyLoopMode = sc.isAnyLoopMode();
- });
-
/* If you're recording (input or actions) no menu is allowed; you can't do
anything, especially deallocate the channel. */
Fl_Menu_Item rclick_menu[] = {
{"Input monitor", 0, menuCallback, (void*) Menu::INPUT_MONITOR,
- FL_MENU_TOGGLE | FL_MENU_DIVIDER | (inputMonitor ? FL_MENU_VALUE : 0)},
+ FL_MENU_TOGGLE | (m_channel.sample->a_getInputMonitor() ? FL_MENU_VALUE : 0)},
+ {"Overdub protection", 0, menuCallback, (void*) Menu::OVERDUB_PROTECTION,
+ FL_MENU_TOGGLE | FL_MENU_DIVIDER | (m_channel.sample->a_getOverdubProtection() ? FL_MENU_VALUE : 0)},
{"Load new sample...", 0, menuCallback, (void*) Menu::LOAD_SAMPLE},
{"Export sample to file...", 0, menuCallback, (void*) Menu::EXPORT_SAMPLE},
{"Setup keyboard input...", 0, menuCallback, (void*) Menu::SETUP_KEYBOARD_INPUT},
{0}
};
- if (isEmptyOrMissing) {
+ 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 (!hasActions)
+ 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 (isAnyLoopMode)
+ if (m_channel.sample->isLoop)
rclick_menu[(int) Menu::CLEAR_ACTIONS_START_STOP].deactivate();
Fl_Menu_Button b(0, 0, 100, 50);
void geSampleChannel::cb_readActions()
{
- c::channel::toggleReadingActions(channelId);
+ if (Fl::event_shift())
+ c::events::killReadActionsChannel(m_channel.id, Thread::MAIN);
+ else
+ c::events::toggleReadActionsChannel(m_channel.id, Thread::MAIN);
}
{
geChannel::refresh();
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- if (c.hasData())
- status->redraw();
- if (c.hasActions) {
- readActions->activate();
- readActions->setStatus(c.readActions);
- }
+ if (m_channel.sample->waveId != 0) {
+ status->redraw();
+ if (m_channel.sample->a_getOverdubProtection())
+ arm->deactivate();
else
- readActions->deactivate();
- });
+ arm->activate();
+ }
+
+ if (m_channel.hasActions) {
+ readActions->activate();
+ readActions->setStatus(m_channel.a_getReadActions());
+ }
+ else
+ readActions->deactivate();
}
#define GE_SAMPLE_CHANNEL_H
+#include "glue/channel.h"
#include "channel.h"
namespace giada {
-namespace m
-{
-class SampleChannel;
-}
namespace v
{
class geChannelMode;
-
class geSampleChannel : public geChannel
{
public:
- geSampleChannel(int x, int y, int w, int h, ID channelId);
+ 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;
private:
- static void cb_playButton(Fl_Widget* v, void* p);
- static void cb_openMenu(Fl_Widget* v, void* p);
- static void cb_readActions(Fl_Widget* v, void* p);
+ 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();
#include <FL/Fl.H>
-#include "core/channels/sampleChannel.h"
-#include "core/const.h"
-#include "core/model/model.h"
-#include "core/wave.h"
-#include "core/mixer.h"
-#include "core/recorder.h"
-#include "core/recManager.h"
#include "utils/string.h"
#include "utils/fs.h"
#include "glue/channel.h"
namespace giada {
namespace v
{
-geSampleChannelButton::geSampleChannelButton(int x, int y, int w, int h, ID channelId)
-: geChannelButton(x, y, w, h, channelId)
+geSampleChannelButton::geSampleChannelButton(int x, int y, int w, int h, const c::channel::Data& d)
+: geChannelButton(x, y, w, h, d)
{
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- const m::SampleChannel& sc = static_cast<m::SampleChannel&>(c);
-
- switch (sc.playStatus) {
- case ChannelStatus::EMPTY:
- label("-- no sample --");
- break;
- case ChannelStatus::MISSING:
- case ChannelStatus::WRONG:
- label("* file not found! *");
- break;
- default:
- if (sc.name.empty()) {
- m::model::onGet(m::model::waves, sc.waveId, [&](m::Wave& w)
- {
- label(w.getBasename(false).c_str());
- });
- }
- else
- label(sc.name.c_str());
- break;
- }
- });
+ switch (m_channel.a_getPlayStatus()) {
+ case ChannelStatus::MISSING:
+ case ChannelStatus::WRONG:
+ label("* file not found! *");
+ break;
+ default:
+ label(m_channel.sample->waveId == 0 ? "-- no sample --" : m_channel.name.c_str());
+ break;
+ }
}
void geSampleChannelButton::refresh()
{
geChannelButton::refresh();
-
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- if (m::recManager::isRecordingInput() && c.armed)
- setInputRecordMode();
- else
- if (m::recManager::isRecordingAction() && c.hasData())
- setActionRecordMode();
- });
+
+ if (m_channel.a_isRecordingInput() && m_channel.a_isArmed())
+ setInputRecordMode();
+ else
+ if (m_channel.a_isRecordingAction() && m_channel.sample->waveId != 0 && !m_channel.sample->isLoop)
+ setActionRecordMode();
redraw();
}
break;
}
case FL_PASTE: {
- c::channel::loadChannel(m_channelId, u::string::trim(u::fs::stripFileUrl(Fl::event_text())));
+ c::channel::loadChannel(m_channel.id, u::string::trim(u::fs::stripFileUrl(Fl::event_text())));
ret = 1;
break;
}
namespace giada {
-namespace m
-{
-class SampleChannel;
-}
namespace v
{
class geSampleChannelButton : public geChannelButton
{
public:
- geSampleChannelButton(int x, int y, int w, int h, ID channelId);
+ geSampleChannelButton(int x, int y, int w, int h, const c::channel::Data& d);
int handle(int e) override;
#include "core/const.h"
-#include "core/model/model.h"
#include "core/graphics.h"
-#include "core/mixer.h"
-#include "core/mixerHandler.h"
-#include "core/pluginHost.h"
+#include "glue/events.h"
#include "glue/main.h"
+#include "glue/channel.h"
#include "utils/gui.h"
#include "gui/elems/soundMeter.h"
#include "gui/elems/basics/statusButton.h"
namespace v
{
geMainIO::geMainIO(int x, int y)
-: Fl_Pack(x, y, 396, G_GUI_UNIT)
+: gePack (x, y, Direction::HORIZONTAL)
+, outMeter (0, 0, 140, G_GUI_UNIT)
+, inMeter (0, 0, 140, G_GUI_UNIT)
+, outVol (0, 0, G_GUI_UNIT, G_GUI_UNIT)
+, inVol (0, 0, G_GUI_UNIT, G_GUI_UNIT)
+, inToOut (0, 0, 12, G_GUI_UNIT, "")
+#ifdef WITH_VST
+, masterFxOut(0, 0, G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm)
+, masterFxIn (0, 0, G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm)
+#endif
{
- type(Fl_Pack::HORIZONTAL);
- spacing(G_GUI_INNER_MARGIN);
-
- begin();
-
-#if defined(WITH_VST)
-
- masterFxIn = new geStatusButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm);
- inVol = new geDial (0, 0, G_GUI_UNIT, G_GUI_UNIT);
- inMeter = new geSoundMeter (0, 0, 140, G_GUI_UNIT);
- inToOut = new geButton (0, 0, 12, G_GUI_UNIT, "");
- outMeter = new geSoundMeter (0, 0, 140, G_GUI_UNIT);
- outVol = new geDial (0, 0, G_GUI_UNIT, G_GUI_UNIT);
- masterFxOut = new geStatusButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, fxOff_xpm, fxOn_xpm);
-
-#else
-
- inVol = new geDial (0, 0, G_GUI_UNIT, G_GUI_UNIT);
- inMeter = new geSoundMeter(0, 0, 140, G_GUI_UNIT);
- outMeter = new geSoundMeter(0, 0, 140, G_GUI_UNIT);
- outVol = new geDial (0, 0, G_GUI_UNIT, G_GUI_UNIT);
-
+#ifdef WITH_VST
+ add(&masterFxIn);
+#endif
+ add(&inVol);
+ add(&inMeter);
+ add(&inToOut);
+ add(&outMeter);
+ add(&outVol);
+#ifdef WITH_VST
+ add(&masterFxOut);
#endif
-
- end();
resizable(nullptr); // don't resize any widget
- outVol->callback(cb_outVol, (void*)this);
- inVol->callback(cb_inVol, (void*)this);
+ outVol.callback(cb_outVol, (void*)this);
+ inVol.callback(cb_inVol, (void*)this);
- outVol->value(m::mh::getOutVol());
- inVol->value(m::mh::getInVol());
+ inToOut.callback(cb_inToOut, (void*)this);
+ inToOut.type(FL_TOGGLE_BUTTON);
#ifdef WITH_VST
-
- masterFxOut->callback(cb_masterFxOut, (void*)this);
- masterFxIn->callback(cb_masterFxIn, (void*)this);
- inToOut->callback(cb_inToOut, (void*)this);
- inToOut->type(FL_TOGGLE_BUTTON);
-
+ masterFxOut.callback(cb_masterFxOut, (void*)this);
+ masterFxIn.callback(cb_masterFxIn, (void*)this);
#endif
}
/* -------------------------------------------------------------------------- */
-void geMainIO::cb_outVol (Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_outVol(); }
-void geMainIO::cb_inVol (Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_inVol(); }
+void geMainIO::cb_outVol (Fl_Widget* /*w*/, void* p) { ((geMainIO*)p)->cb_outVol(); }
+void geMainIO::cb_inVol (Fl_Widget* /*w*/, void* p) { ((geMainIO*)p)->cb_inVol(); }
+void geMainIO::cb_inToOut (Fl_Widget* /*w*/, void* p) { ((geMainIO*)p)->cb_inToOut(); }
#ifdef WITH_VST
-void geMainIO::cb_masterFxOut(Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_masterFxOut(); }
-void geMainIO::cb_masterFxIn (Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_masterFxIn(); }
-void geMainIO::cb_inToOut (Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_inToOut(); }
+void geMainIO::cb_masterFxOut(Fl_Widget* /*w*/, void* p) { ((geMainIO*)p)->cb_masterFxOut(); }
+void geMainIO::cb_masterFxIn (Fl_Widget* /*w*/, void* p) { ((geMainIO*)p)->cb_masterFxIn(); }
#endif
void geMainIO::cb_outVol()
{
- c::main::setOutVol(outVol->value());
+ c::events::setMasterOutVolume(outVol.value(), Thread::MAIN);
}
-/* -------------------------------------------------------------------------- */
-
-
void geMainIO::cb_inVol()
{
- c::main::setInVol(inVol->value());
+ c::events::setMasterInVolume(inVol.value(), Thread::MAIN);
}
+void geMainIO::cb_inToOut()
+{
+ c::main::setInToOut(inToOut.value());
+}
+
/* -------------------------------------------------------------------------- */
u::gui::openSubWindow(G_MainWin, new v::gdPluginList(m::mixer::MASTER_IN_CHANNEL_ID), WID_FX_LIST);
}
-
-void geMainIO::cb_inToOut()
-{
- m::mh::setInToOut(inToOut->value());
-}
-
#endif
void geMainIO::setOutVol(float v)
{
- outVol->value(v);
+ outVol.value(v);
}
void geMainIO::setInVol(float v)
{
- inVol->value(v);
+ inVol.value(v);
}
void geMainIO::setMasterFxOutFull(bool v)
{
- masterFxOut->setStatus(v);
+ masterFxOut.setStatus(v);
}
void geMainIO::setMasterFxInFull(bool v)
{
- masterFxIn->setStatus(v);
+ masterFxIn.setStatus(v);
}
#endif
void geMainIO::refresh()
{
- outMeter->mixerPeak = m::mixer::peakOut.load();
- inMeter->mixerPeak = m::mixer::peakIn.load();
- outMeter->redraw();
- inMeter->redraw();
+ outMeter.mixerPeak = m_io.a_getMasterOutPeak();
+ inMeter.mixerPeak = m_io.a_getMasterInPeak();
+ outMeter.redraw();
+ inMeter.redraw();
}
void geMainIO::rebuild()
{
- m::model::onGet(m::model::channels, m::mixer::MASTER_OUT_CHANNEL_ID, [&](m::Channel& c)
- {
- outVol->value(c.volume);
-#ifdef WITH_VST
- masterFxOut->setStatus(c.pluginIds.size() > 0);
-#endif
- });
+ m_io = c::main::getIO();
- m::model::onGet(m::model::channels, m::mixer::MASTER_IN_CHANNEL_ID, [&](m::Channel& c)
- {
- inVol->value(c.volume);
+ outVol.value(m_io.masterOutVol);
+ inVol.value(m_io.masterInVol);
#ifdef WITH_VST
- masterFxIn->setStatus(c.pluginIds.size() > 0);
+ masterFxOut.setStatus(m_io.masterOutHasPlugins);
+ masterFxIn.setStatus(m_io.masterInHasPlugins);
+ inToOut.value(m_io.inToOut);
#endif
- });
}
}} // giada::v::
#define GE_MAIN_IO_H
-#include <FL/Fl_Pack.H>
-
-
-class geSoundMeter;
-class geDial;
+#include "gui/elems/soundMeter.h"
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/dial.h"
+#include "gui/elems/basics/button.h"
#ifdef WITH_VST
-class geStatusButton;
-class geButton;
+#include "gui/elems/basics/statusButton.h"
#endif
+#include "glue/main.h"
namespace giada {
namespace v
{
-class geMainIO : public Fl_Pack
+class geMainIO : public gePack
{
public:
private:
- static void cb_outVol (Fl_Widget* v, void* p);
- static void cb_inVol (Fl_Widget* v, void* p);
-#ifdef WITH_VST
- static void cb_masterFxOut(Fl_Widget* v, void* p);
- static void cb_masterFxIn (Fl_Widget* v, void* p);
- static void cb_inToOut (Fl_Widget* v, void* p);
-#endif
+ static void cb_outVol (Fl_Widget* /*w*/, void* p);
+ static void cb_inVol (Fl_Widget* /*w*/, void* p);
+ static void cb_inToOut(Fl_Widget* /*w*/, void* p);
void cb_outVol();
void cb_inVol();
+ void cb_inToOut();
#ifdef WITH_VST
+ static void cb_masterFxOut(Fl_Widget* /*w*/, void* p);
+ static void cb_masterFxIn (Fl_Widget* /*w*/, void* p);
void cb_masterFxOut();
void cb_masterFxIn();
- void cb_inToOut();
#endif
- geSoundMeter* outMeter;
- geSoundMeter* inMeter;
- geDial* outVol;
- geDial* inVol;
+ c::main::IO m_io;
+
+ geSoundMeter outMeter;
+ geSoundMeter inMeter;
+ geDial outVol;
+ geDial inVol;
+ geButton inToOut;
#ifdef WITH_VST
- geStatusButton* masterFxOut;
- geStatusButton* masterFxIn;
- geButton* inToOut;
+ geStatusButton masterFxOut;
+ geStatusButton masterFxIn;
#endif
};
}} // giada::v::
#include <cassert>
#include <FL/Fl_Menu_Button.H>
-#include "core/channels/sampleChannel.h"
-#include "core/channels/channel.h"
#include "core/model/model.h"
#include "core/const.h"
#include "core/mixer.h"
namespace v
{
geMainMenu::geMainMenu(int x, int y)
-: Fl_Pack(x, y, 300, 20)
+: gePack(x, y, Direction::HORIZONTAL)
{
- type(Fl_Pack::HORIZONTAL);
- spacing(G_GUI_INNER_MARGIN);
-
- begin();
-
file = new geButton(0, 0, 70, 21, "file");
edit = new geButton(0, 0, 70, 21, "edit");
config = new geButton(0, 0, 70, 21, "config");
about = new geButton(0, 0, 70, 21, "about");
-
- end();
+ add(file);
+ add(edit);
+ add(config);
+ add(about);
resizable(nullptr); // don't resize any widget
file->callback(cb_file, (void*)this);
edit->callback(cb_edit, (void*)this);
- about->callback([](Fl_Widget* w, void* v) {
+ about->callback([](Fl_Widget* /*w*/, void* /*v*/) {
u::gui::openSubWindow(G_MainWin, new gdAbout(), WID_ABOUT);
});
- config->callback([](Fl_Widget* w, void* v) {
+ config->callback([](Fl_Widget* /*w*/, void* /*v*/) {
u::gui::openSubWindow(G_MainWin, new gdConfig(400, 370), WID_CONFIG);
});
}
/* -------------------------------------------------------------------------- */
-void geMainMenu::cb_file(Fl_Widget* v, void* p) { ((geMainMenu*)p)->cb_file(); }
-void geMainMenu::cb_edit(Fl_Widget* v, void* p) { ((geMainMenu*)p)->cb_edit(); }
+void geMainMenu::cb_file(Fl_Widget* /*w*/, void* p) { ((geMainMenu*)p)->cb_file(); }
+void geMainMenu::cb_edit(Fl_Widget* /*w*/, void* p) { ((geMainMenu*)p)->cb_edit(); }
/* -------------------------------------------------------------------------- */
}
else
if (strcmp(m->label(), "Close project") == 0) {
- c::main::resetToInitState(/*createColumns=*/true);
+ c::main::closeProject();
}
#ifndef NDEBUG
else
void geMainMenu::cb_edit()
{
Fl_Menu_Item menu[] = {
- {"Clear all samples"},
+ {"Free all Sample channels"},
{"Clear all actions"},
{"Setup global MIDI input..."},
{0}
const Fl_Menu_Item* m = menu->popup(Fl::event_x(), Fl::event_y(), 0, 0, &b);
if (!m) return;
- if (strcmp(m->label(), "Clear all samples") == 0)
+ if (strcmp(m->label(), "Free all Sample channels") == 0)
c::main::clearAllSamples();
else
if (strcmp(m->label(), "Clear all actions") == 0)
#define GE_MAIN_MENU_H
-#include <FL/Fl_Pack.H>
+#include "gui/elems/basics/pack.h"
class geButton;
namespace giada {
namespace v
{
-class geMainMenu : public Fl_Pack
+class geMainMenu : public gePack
{
public:
private:
- static void cb_file(Fl_Widget* v, void* p);
- static void cb_edit(Fl_Widget* v, void* p);
+ static void cb_file(Fl_Widget* /*w*/, void* p);
+ static void cb_edit(Fl_Widget* /*w*/, void* p);
void cb_file();
void cb_edit();
#include "core/const.h"
-#include "core/model/model.h"
-#include "core/kernelAudio.h"
-#include "core/mixer.h"
-#include "core/recManager.h"
#include "core/graphics.h"
#include "core/clock.h"
#include "glue/main.h"
+#include "glue/events.h"
#include "utils/gui.h"
#include "utils/string.h"
#include "gui/elems/basics/button.h"
namespace v
{
geMainTimer::geMainTimer(int x, int y)
- : Fl_Group(x, y, 210, 20)
+: gePack (x, y, Direction::HORIZONTAL)
+, m_bpm (0, 0, 60, G_GUI_UNIT)
+, m_meter (0, 0, 60, G_GUI_UNIT)
+, m_quantizer (0, 0, 60, G_GUI_UNIT, "", false)
+, m_multiplier(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", multiplyOff_xpm, multiplyOn_xpm)
+, m_divider (0, 0, G_GUI_UNIT, G_GUI_UNIT, "", divideOff_xpm, divideOn_xpm)
{
- begin();
-
- quantizer = new geChoice(x, y, 50, 20, "", false);
- bpm = new geButton(quantizer->x()+quantizer->w()+4, y, 50, 20);
- meter = new geButton(bpm->x()+bpm->w()+8, y, 50, 20);
- multiplier = new geButton(meter->x()+meter->w()+4, y, 20, 20, "", multiplyOff_xpm, multiplyOn_xpm);
- divider = new geButton(multiplier->x()+multiplier->w()+4, y, 20, 20, "", divideOff_xpm, divideOn_xpm);
-
- end();
+ add(&m_quantizer);
+ add(&m_bpm);
+ add(&m_meter);
+ add(&m_multiplier);
+ add(&m_divider);
resizable(nullptr); // don't resize any widget
- bpm->copy_label(u::string::fToString(m::clock::getBpm(), 1).c_str());
- bpm->callback(cb_bpm, (void*)this);
-
- meter->callback(cb_meter, (void*)this);
-
- multiplier->callback(cb_multiplier, (void*)this);
-
- divider->callback(cb_divider, (void*)this);
-
- quantizer->add("off", 0, cb_quantizer, (void*)this);
- quantizer->add("1\\/1", 0, cb_quantizer, (void*)this);
- quantizer->add("1\\/2", 0, cb_quantizer, (void*)this);
- quantizer->add("1\\/3", 0, cb_quantizer, (void*)this);
- quantizer->add("1\\/4", 0, cb_quantizer, (void*)this);
- quantizer->add("1\\/6", 0, cb_quantizer, (void*)this);
- quantizer->add("1\\/8", 0, cb_quantizer, (void*)this);
- quantizer->value(0); // "off" by default
-
-#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
-
- /* Can't change bpm from within Giada when using JACK. */
-
- if (m::kernelAudio::getAPI() == G_SYS_API_JACK)
- bpm->deactivate();
-
-#endif
+ m_bpm.callback(cb_bpm, (void*)this);
+ m_meter.callback(cb_meter, (void*)this);
+ m_multiplier.callback(cb_multiplier, (void*)this);
+ m_divider.callback(cb_divider, (void*)this);
+
+ m_quantizer.add("off", 0, cb_quantizer, (void*)this);
+ m_quantizer.add("1\\/1", 0, cb_quantizer, (void*)this);
+ m_quantizer.add("1\\/2", 0, cb_quantizer, (void*)this);
+ m_quantizer.add("1\\/3", 0, cb_quantizer, (void*)this);
+ m_quantizer.add("1\\/4", 0, cb_quantizer, (void*)this);
+ m_quantizer.add("1\\/6", 0, cb_quantizer, (void*)this);
+ m_quantizer.add("1\\/8", 0, cb_quantizer, (void*)this);
+ m_quantizer.value(0); // "off" by default
}
/* -------------------------------------------------------------------------- */
-void geMainTimer::cb_bpm (Fl_Widget* v, void* p) { ((geMainTimer*)p)->cb_bpm(); }
-void geMainTimer::cb_meter (Fl_Widget* v, void* p) { ((geMainTimer*)p)->cb_meter(); }
-void geMainTimer::cb_quantizer (Fl_Widget* v, void* p) { ((geMainTimer*)p)->cb_quantizer(); }
-void geMainTimer::cb_multiplier(Fl_Widget* v, void* p) { ((geMainTimer*)p)->cb_multiplier(); }
-void geMainTimer::cb_divider (Fl_Widget* v, void* p) { ((geMainTimer*)p)->cb_divider(); }
+void geMainTimer::cb_bpm (Fl_Widget* /*w*/, void* p) { ((geMainTimer*)p)->cb_bpm(); }
+void geMainTimer::cb_meter (Fl_Widget* /*w*/, void* p) { ((geMainTimer*)p)->cb_meter(); }
+void geMainTimer::cb_quantizer (Fl_Widget* /*w*/, void* p) { ((geMainTimer*)p)->cb_quantizer(); }
+void geMainTimer::cb_multiplier(Fl_Widget* /*w*/, void* p) { ((geMainTimer*)p)->cb_multiplier(); }
+void geMainTimer::cb_divider (Fl_Widget* /*w*/, void* p) { ((geMainTimer*)p)->cb_divider(); }
/* -------------------------------------------------------------------------- */
void geMainTimer::cb_bpm()
{
- u::gui::openSubWindow(G_MainWin, new gdBpmInput(bpm->label()), WID_BPM);
+ u::gui::openSubWindow(G_MainWin, new gdBpmInput(m_bpm.label()), WID_BPM);
}
void geMainTimer::cb_quantizer()
{
- c::main::quantize(quantizer->value());
+ c::main::quantize(m_quantizer.value());
}
void geMainTimer::cb_multiplier()
{
- c::main::beatsMultiply();
+ c::events::multiplyBeats();
}
void geMainTimer::cb_divider()
{
- c::main::beatsDivide();
+ c::events::divideBeats();
}
void geMainTimer::refresh()
{
- if (m::recManager::isRecordingInput()) {
- bpm->deactivate();
- meter->deactivate();
- multiplier->deactivate();
- divider->deactivate();
+ m_timer = c::main::getTimer();
+
+ if (m_timer.isRecordingInput) {
+ m_bpm.deactivate();
+ m_meter.deactivate();
+ m_multiplier.deactivate();
+ m_divider.deactivate();
}
else {
- /* Don't reactivate bpm when using JACK. It must stay disabled. */
-
#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
- if (m::kernelAudio::getAPI() != G_SYS_API_JACK)
- bpm->activate();
+ /* Don't reactivate m_bpm when using JACK. It must stay disabled. */
+ if (m_timer.isUsingJack)
+ m_bpm.deactivate();
+ else
+ m_bpm.activate();
#else
- bpm->activate();
+ m_bpm.activate();
#endif
- meter->activate();
- multiplier->activate();
- divider->activate();
+ m_meter.activate();
+ m_multiplier.activate();
+ m_divider.activate();
}
}
void geMainTimer::rebuild()
{
- m::model::onGet(m::model::clock, [&](m::model::Clock& c)
- {
- setBpm(c.bpm);
- setMeter(c.beats, c.bars);
- setQuantizer(c.quantize);
- });
+ m_timer = c::main::getTimer();
+
+ setBpm(m_timer.bpm);
+ setMeter(m_timer.beats, m_timer.bars);
+ setQuantizer(m_timer.quantize);
+
+#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
+ /* Can't change m_bpm from within Giada when using JACK. */
+ if (m_timer.isUsingJack)
+ m_bpm.deactivate();
+#endif
}
void geMainTimer::setBpm(const char* v)
{
- bpm->copy_label(v);
+ m_bpm.copy_label(v);
}
void geMainTimer::setBpm(float v)
{
- bpm->copy_label(u::string::fToString((float) v, 1).c_str()); // Only 1 decimal place (e.g. 120.0)
+ m_bpm.copy_label(u::string::fToString((float) v, 1).c_str()); // Only 1 decimal place (e.g. 120.0)
}
void geMainTimer::setLock(bool v)
{
if (v) {
- bpm->deactivate();
- meter->deactivate();
- multiplier->deactivate();
- divider->deactivate();
+ m_bpm.deactivate();
+ m_meter.deactivate();
+ m_multiplier.deactivate();
+ m_divider.deactivate();
}
else {
- bpm->activate();
- meter->activate();
- multiplier->activate();
- divider->activate();
+ m_bpm.activate();
+ m_meter.activate();
+ m_multiplier.activate();
+ m_divider.activate();
}
}
void geMainTimer::setQuantizer(int q)
{
- quantizer->value(q);
+ m_quantizer.value(q);
}
void geMainTimer::setMeter(int beats, int bars)
{
std::string s = std::to_string(beats) + "/" + std::to_string(bars);
- meter->copy_label(s.c_str());
+ m_meter.copy_label(s.c_str());
}
}} // giada::v::
#define GE_MAIN_TIMER_H
-#include <FL/Fl_Group.H>
+#include "glue/main.h"
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/choice.h"
class geButton;
-class geChoice;
namespace giada {
namespace v
{
-class geMainTimer : public Fl_Group
+class geChoice;
+class geMainTimer : public gePack
{
public:
void setQuantizer(int q);
/* setLock
- Locks bpm, beter and multipliers. Used during audio recordings. */
+ Locks bpm, meter and multipliers. Used during audio recordings. */
void setLock(bool v);
private:
- static void cb_bpm (Fl_Widget* v, void* p);
- static void cb_meter (Fl_Widget* v, void* p);
- static void cb_quantizer (Fl_Widget* v, void* p);
- static void cb_multiplier(Fl_Widget* v, void* p);
- static void cb_divider (Fl_Widget* v, void* p);
+ static void cb_bpm (Fl_Widget* /*w*/, void* p);
+ static void cb_meter (Fl_Widget* /*w*/, void* p);
+ static void cb_quantizer (Fl_Widget* /*w*/, void* p);
+ static void cb_multiplier(Fl_Widget* /*w*/, void* p);
+ static void cb_divider (Fl_Widget* /*w*/, void* p);
void cb_bpm();
void cb_meter();
void cb_quantizer();
void cb_multiplier();
void cb_divider();
- geButton* bpm;
- geButton* meter;
- geChoice* quantizer;
- geButton* multiplier;
- geButton* divider;
+ c::main::Timer m_timer;
+
+ geButton m_bpm;
+ geButton m_meter;
+ geChoice m_quantizer;
+ geButton m_multiplier;
+ geButton m_divider;
};
}} // giada::v::
#include "core/graphics.h"
#include "core/conf.h"
#include "core/clock.h"
+#include "core/sequencer.h"
#include "core/mixer.h"
#include "core/mixerHandler.h"
#include "core/recManager.h"
#include "core/conf.h"
#include "core/const.h"
#include "glue/main.h"
+#include "glue/events.h"
#include "gui/elems/basics/button.h"
#include "gui/elems/basics/box.h"
#include "gui/elems/basics/statusButton.h"
namespace v
{
geMainTransport::geMainTransport(int x, int y)
-: Fl_Pack(x, y, 200, 25)
+: gePack(x, y, Direction::HORIZONTAL)
{
- type(Fl_Pack::HORIZONTAL);
- spacing(G_GUI_INNER_MARGIN);
-
rewind = new geButton (0, 0, 25, 25, "", rewindOff_xpm, rewindOn_xpm);
play = new geStatusButton(0, 0, 25, 25, play_xpm, pause_xpm);
- new geBox (0, 0, 10, 25);
- recTriggerMode = new geButton (0, 0, 15, 25, "", recTriggerModeOff_xpm, recTriggerModeOn_xpm);
+ spacer1 = new geBox (0, 0, 10, 25);
+ recTriggerMode = new geStatusButton(0, 0, 15, 25, recTriggerModeOff_xpm, recTriggerModeOn_xpm);
recAction = new geStatusButton(0, 0, 25, 25, recOff_xpm, recOn_xpm);
recInput = new geStatusButton(0, 0, 25, 25, inputRecOff_xpm, inputRecOn_xpm);
- new geBox (0, 0, 10, 25);
+ spacer2 = new geBox (0, 0, 10, 25);
metronome = new geStatusButton(0, 0, 15, 25, metronomeOff_xpm, metronomeOn_xpm);
-
- rewind->callback([](Fl_Widget* w, void* v) {
- m::mh::rewindSequencer();
+ add(rewind);
+ add(play);
+ add(spacer1);
+ add(recTriggerMode);
+ add(recAction);
+ add(recInput);
+ add(spacer2);
+ add(metronome);
+
+ rewind->callback([](Fl_Widget* /*w*/, void* /*v*/) {
+ c::events::rewindSequencer(Thread::MAIN);
});
- play->callback([](Fl_Widget* w, void* v) {
- m::mh::toggleSequencer();
+ play->callback([](Fl_Widget* /*w*/, void* /*v*/) {
+ c::events::toggleSequencer(Thread::MAIN);
});
- recAction->callback([](Fl_Widget* w, void* v) {
- c::main::toggleActionRec();
+ recAction->callback([](Fl_Widget* /*w*/, void* /*v*/) {
+ c::events::toggleActionRecording();
});
- recInput->callback([](Fl_Widget* w, void* v) {
- c::main::toggleInputRec();
+ recInput->callback([](Fl_Widget* /*w*/, void* /*v*/) {
+ c::events::toggleInputRecording();
});
- recTriggerMode->value(static_cast<int>(m::conf::conf.recTriggerMode));
- recTriggerMode->type(FL_TOGGLE_BUTTON);
- recTriggerMode->callback([](Fl_Widget* w, void* v) {
- m::conf::conf.recTriggerMode = static_cast<RecTriggerMode>(static_cast<geButton*>(w)->value());
+ recTriggerMode->callback([](Fl_Widget* /*w*/, void* /*v*/) {
+ c::main::toggleRecOnSignal();
});
metronome->type(FL_TOGGLE_BUTTON);
- metronome->callback([](Fl_Widget* w, void* v) {
- m::mixer::toggleMetronome();
+ metronome->callback([](Fl_Widget* /*w*/, void* /*v*/) {
+ c::events::toggleMetronome();
});
}
play->setStatus(m::clock::isRunning());
recAction->setStatus(m::recManager::isRecordingAction());
recInput->setStatus(m::recManager::isRecordingInput());
- metronome->setStatus(m::mixer::isMetronomeOn());
+ metronome->setStatus(m::sequencer::isMetronomeOn());
+ recTriggerMode->setStatus(m::conf::conf.recTriggerMode == RecTriggerMode::SIGNAL);
}
-
}} // giada::v::
#define GE_MAIN_TRANSPORT_H
-#include <FL/Fl_Pack.H>
+#include "gui/elems/basics/pack.h"
class geButton;
+class geBox;
class geStatusButton;
namespace giada {
namespace v
{
-class geMainTransport : public Fl_Pack
+class geMainTransport : public gePack
{
public:
geButton* rewind;
geStatusButton* play;
-
- geButton* recTriggerMode;
+ geBox* spacer1;
+ geStatusButton* recTriggerMode;
geStatusButton* recAction;
geStatusButton* recInput;
-
+ geBox* spacer2;
geStatusButton* metronome;
};
}} // giada::v::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cassert>
+#include "utils/string.h"
+#include "gui/elems/basics/boxtypes.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
+#include "midiLearner.h"
+
+
+namespace giada {
+namespace v
+{
+geMidiLearner::geMidiLearner(int x, int y, std::string l, int param)
+: gePack (x, y, Direction::HORIZONTAL)
+, onStartLearn(nullptr)
+, onStopLearn (nullptr)
+, onClearLearn(nullptr)
+, m_param (param)
+, m_text (0, 0, 146, 20, l.c_str())
+, m_valueBtn (0, 0, 80, 20)
+, m_button (0, 0, 50, 20, "learn")
+{
+ add(&m_text);
+ add(&m_valueBtn);
+ add(&m_button);
+
+ m_text.box(G_CUSTOM_BORDER_BOX);
+
+ m_valueBtn.box(G_CUSTOM_BORDER_BOX);
+ m_valueBtn.callback(cb_value, (void*)this);
+ m_valueBtn.when(FL_WHEN_RELEASE);
+
+ m_button.type(FL_TOGGLE_BUTTON);
+ m_button.callback(cb_button, (void*)this);
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geMidiLearner::cb_button(Fl_Widget* /*w*/, void* p) { ((geMidiLearner*)p)->onLearn(); }
+void geMidiLearner::cb_value(Fl_Widget* /*w*/, void* p) { ((geMidiLearner*)p)->onReset(); }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geMidiLearner::update(uint32_t value)
+{
+ std::string tmp = "(not set)";
+
+ 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::activate()
+{
+ Fl_Group::activate();
+ m_valueBtn.activate();
+ m_button.activate();
+}
+
+
+void geMidiLearner::deactivate()
+{
+ Fl_Group::deactivate();
+ m_valueBtn.deactivate();
+ m_button.deactivate();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geMidiLearner::onLearn() const
+{
+ assert(onStartLearn != nullptr);
+ assert(onStopLearn != nullptr);
+
+ if (m_button.value() == 1)
+ onStartLearn(m_param);
+ else
+ onStopLearn();
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geMidiLearner::onReset() const
+{
+ assert(onClearLearn != nullptr);
+
+ if (Fl::event_button() == FL_RIGHT_MOUSE)
+ onClearLearn(m_param);
+}
+}} // giada::v::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_MIDI_LEARNER_H
+#define GE_MIDI_LEARNER_H
+
+
+#include <functional>
+#include <string>
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/button.h"
+
+
+class geBox;
+class geButton;
+
+
+namespace giada {
+namespace v
+{
+class geMidiLearner : public gePack
+{
+public:
+
+ geMidiLearner(int x, int y, std::string l, int param);
+
+ /* update
+ Updates and repaints the label widget with value 'value'. */
+
+ void update(uint32_t value);
+
+ void activate();
+ void deactivate();
+
+ std::function<void(uint32_t)> onStartLearn;
+ std::function<void()> onStopLearn;
+ std::function<void(uint32_t)> onClearLearn;
+
+protected:
+
+ /* m_param
+ Parameter index to be learnt. */
+
+ int m_param;
+
+ geBox m_text;
+ geButton m_valueBtn;
+ geButton m_button;
+
+private:
+
+ static void cb_button(Fl_Widget* /*w*/, void* p);
+ static void cb_value (Fl_Widget* /*w*/, void* p);
+
+ void onLearn() const;
+ void onReset() const;
+
+};
+}} // giada::v::
+
+
+#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <string>
-#include "utils/string.h"
-#include "gui/elems/basics/boxtypes.h"
-#include "gui/elems/basics/box.h"
-#include "gui/elems/basics/button.h"
-#include "midiLearnerBase.h"
-
-
-namespace giada {
-namespace v
-{
-geMidiLearnerBase::geMidiLearnerBase(int X, int Y, int W, std::string l, int param, uint32_t value)
-: Fl_Group (X, Y, W, 20),
- m_param (param)
-{
- begin();
- m_text = new geBox(x(), y(), 156, 20, l.c_str());
- m_valueBtn = new geButton(m_text->x()+m_text->w()+4, y(), 80, 20);
- m_button = new geButton(m_valueBtn->x()+m_valueBtn->w()+4, y(), 40, 20, "learn");
- end();
-
- m_text->box(G_CUSTOM_BORDER_BOX);
- m_text->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
-
- m_valueBtn->box(G_CUSTOM_BORDER_BOX);
- m_valueBtn->callback(cb_value, (void*)this);
- m_valueBtn->when(FL_WHEN_RELEASE);
-
- m_button->type(FL_TOGGLE_BUTTON);
- m_button->callback(cb_button, (void*)this);
-
- update(value);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geMidiLearnerBase::cb_button(Fl_Widget* v, void* p) { ((geMidiLearnerBase*)p)->onLearn(); }
-void geMidiLearnerBase::cb_value(Fl_Widget* v, void* p) { ((geMidiLearnerBase*)p)->onReset(); }
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geMidiLearnerBase::update(uint32_t value)
-{
- std::string tmp = "(not set)";
-
- 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 geMidiLearnerBase::activate()
-{
- Fl_Group::activate();
- m_valueBtn->activate();
- m_button->activate();
-}
-
-
-void geMidiLearnerBase::deactivate()
-{
- Fl_Group::deactivate();
- m_valueBtn->deactivate();
- m_button->deactivate();
-}
-}} // giada::v::
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_MIDI_LEARNER_BASE_H
-#define GE_MIDI_LEARNER_BASE_H
-
-
-#include <string>
-#include <FL/Fl_Group.H>
-
-
-class geBox;
-class geButton;
-
-
-namespace giada {
-namespace v
-{
-class geMidiLearnerBase : public Fl_Group
-{
-public:
-
- virtual ~geMidiLearnerBase() = default;
-
- virtual void refresh() = 0;
- virtual void onLearn() = 0;
- virtual void onReset() = 0;
-
- void activate();
- void deactivate();
-
-protected:
-
- geMidiLearnerBase(int x, int y, int w, std::string l, int param, uint32_t value);
-
- /* update
- Updates and repaints the label widget with value 'value'. */
-
- void update(uint32_t value);
-
- /* m_param
- Parameter index to be learnt. */
-
- int m_param;
-
- geBox* m_text;
- geButton* m_valueBtn;
- geButton* m_button;
-
-private:
-
- static void cb_button(Fl_Widget* v, void* p);
- static void cb_value (Fl_Widget* v, void* p);
-};
-}} // giada::v::
-
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <FL/Fl.H>
-#include "core/model/model.h"
-#include "core/channels/sampleChannel.h"
-#include "glue/io.h"
-#include "gui/elems/basics/button.h"
-#include "midiLearnerChannel.h"
-
-
-namespace giada {
-namespace v
-{
-geMidiLearnerChannel::geMidiLearnerChannel(int x, int y, int w, std::string l, int param, uint32_t value, ID channelId)
-: geMidiLearnerBase(x, y, w, l, param, value),
- m_channelId (channelId)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geMidiLearnerChannel::refresh()
-{
- m::model::onGet(m::model::channels, m_channelId, [&](const m::Channel& c)
- {
- switch (m_param) {
- case G_MIDI_IN_KEYPRESS : update(c.midiInKeyPress); break;
- case G_MIDI_IN_KEYREL : update(c.midiInKeyRel); break;
- case G_MIDI_IN_KILL : update(c.midiInKill); break;
- case G_MIDI_IN_ARM : update(c.midiInArm); break;
- case G_MIDI_IN_MUTE : update(c.midiInVolume); break;
- case G_MIDI_IN_SOLO : update(c.midiInMute); break;
- case G_MIDI_IN_VOLUME : update(c.midiInSolo); break;
- case G_MIDI_IN_PITCH : update(static_cast<const m::SampleChannel&>(c).midiInPitch); break;
- case G_MIDI_IN_READ_ACTIONS : update(static_cast<const m::SampleChannel&>(c).midiInReadActions); break;
- case G_MIDI_OUT_L_PLAYING : update(static_cast<const m::SampleChannel&>(c).midiOutLplaying); break;
- case G_MIDI_OUT_L_MUTE : update(static_cast<const m::SampleChannel&>(c).midiOutLmute); break;
- case G_MIDI_OUT_L_SOLO : update(static_cast<const m::SampleChannel&>(c).midiOutLsolo); break;
- }
- });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geMidiLearnerChannel::onLearn()
-{
- if (m_button->value() == 1)
- c::io::startChannelMidiLearn(m_param, m_channelId);
- else
- c::io::stopMidiLearn();
-}
-
-
-void geMidiLearnerChannel::onReset()
-{
- if (Fl::event_button() == FL_RIGHT_MOUSE)
- c::io::clearChannelMidiLearn(m_param, m_channelId);
-}
-}} // giada::v::
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_MIDI_LEARNER_CHANNEL_H
-#define GE_MIDI_LEARNER_CHANNEL_H
-
-
-#include "midiLearnerBase.h"
-
-
-namespace giada {
-namespace v
-{
-class geMidiLearnerChannel : public geMidiLearnerBase
-{
-public:
-
- geMidiLearnerChannel(int x, int y, int w, std::string l, int param, uint32_t value, ID channelId);
-
- void refresh() override;
- void onLearn() override;
- void onReset() override;
-
-private:
-
- ID m_channelId;
-};
-}} // giada::v::
-
-
-#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#include <FL/Fl.H>
-#include "core/model/model.h"
-#include "glue/io.h"
-#include "gui/elems/basics/button.h"
-#include "midiLearnerMaster.h"
-
-
-namespace giada {
-namespace v
-{
-geMidiLearnerMaster::geMidiLearnerMaster(int X, int Y, int W, std::string l, int param, uint32_t value)
-: geMidiLearnerBase(X, Y, W, l, param, value)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geMidiLearnerMaster::refresh()
-{
- m::model::onGet(m::model::midiIn, [&](const m::model::MidiIn& m)
- {
- switch (m_param) {
- case G_MIDI_IN_REWIND : update(m.rewind); break;
- case G_MIDI_IN_START_STOP : update(m.startStop); break;
- case G_MIDI_IN_ACTION_REC : update(m.actionRec); break;
- case G_MIDI_IN_INPUT_REC : update(m.inputRec); break;
- case G_MIDI_IN_METRONOME : update(m.volumeIn); break;
- case G_MIDI_IN_VOLUME_IN : update(m.volumeOut); break;
- case G_MIDI_IN_VOLUME_OUT : update(m.beatDouble); break;
- case G_MIDI_IN_BEAT_DOUBLE : update(m.beatHalf); break;
- case G_MIDI_IN_BEAT_HALF : update(m.metronome); break;
- }
- });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geMidiLearnerMaster::onLearn()
-{
- if (m_button->value() == 1)
- c::io::startMasterMidiLearn(m_param);
- else
- c::io::stopMidiLearn();
-}
-
-
-void geMidiLearnerMaster::onReset()
-{
- if (Fl::event_button() == FL_RIGHT_MOUSE)
- c::io::clearMasterMidiLearn(m_param);
-}
-}} // giada::v::
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifndef GE_MIDI_LEARNER_MASTER_H
-#define GE_MIDI_LEARNER_MASTER_H
-
-
-#include "midiLearnerBase.h"
-
-
-namespace giada {
-namespace v
-{
-class geMidiLearnerMaster : public geMidiLearnerBase
-{
-public:
-
- geMidiLearnerMaster(int x, int y, int w, std::string l, int param, uint32_t value);
-
- void refresh() override;
- void onLearn() override;
- void onReset() override;
-};
-}} // giada::v::
-
-
-#endif
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#include <cassert>
+#include "core/const.h"
+#include "glue/io.h"
+#include "gui/elems/basics/box.h"
+#include "midiLearnerPack.h"
+
+
+namespace giada {
+namespace v
+{
+constexpr int LEARNER_WIDTH = 284;
+
+
+/* -------------------------------------------------------------------------- */
+
+
+geMidiLearnerPack::geMidiLearnerPack(int X, int Y, std::string title)
+: gePack(X, Y, Direction::VERTICAL)
+{
+ end();
+
+ if (title != "") {
+ geBox* header = new geBox(0, 0, LEARNER_WIDTH, G_GUI_UNIT, title.c_str());
+ header->box(FL_BORDER_BOX);
+ add(header);
+ }
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geMidiLearnerPack::setCallbacks(std::function<void(uint32_t)> s, std::function<void(uint32_t)> c)
+{
+ m_onStartLearn = s;
+ m_onClearLearn = c;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+void geMidiLearnerPack::addMidiLearner(std::string label, int param, bool visible)
+{
+ geMidiLearner* l = new geMidiLearner(0, 0, label, param);
+
+ l->onStartLearn = m_onStartLearn;
+ l->onClearLearn = m_onClearLearn;
+ l->onStopLearn = [] () { c::io::stopMidiLearn(); };
+
+ 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();
+}
+}} // giada::v::
--- /dev/null
+/* -----------------------------------------------------------------------------
+ *
+ * Giada - Your Hardcore Loopmachine
+ *
+ * -----------------------------------------------------------------------------
+ *
+ * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
+ *
+ * This file is part of Giada - Your Hardcore Loopmachine.
+ *
+ * Giada - Your Hardcore Loopmachine is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation, either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Giada - Your Hardcore Loopmachine is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Giada - Your Hardcore Loopmachine. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- */
+
+
+#ifndef GE_LEARNER_PACK_H
+#define GE_LEARNER_PACK_H
+
+
+#include <string>
+#include <vector>
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/midiIO/midiLearner.h"
+
+
+namespace giada {
+namespace v
+{
+class geMidiLearnerPack : public gePack
+{
+public:
+
+ geMidiLearnerPack(int x, int y, std::string title="");
+
+ void setCallbacks(std::function<void(uint32_t)>, std::function<void(uint32_t)>);
+ void addMidiLearner(std::string label, int param, bool visible=true);
+ void setEnabled(bool v);
+
+ std::vector<geMidiLearner*> learners;
+
+private:
+
+ std::function<void(uint32_t)> m_onStartLearn;
+ std::function<void(uint32_t)> m_onClearLearn;
+};
+}} // giada::v::
+
+
+#endif
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-
-#include <FL/Fl.H>
-#include "core/model/model.h"
-#include "glue/io.h"
-#include "gui/elems/basics/button.h"
-#include "midiLearnerPlugin.h"
-
-
-namespace giada {
-namespace v
-{
-geMidiLearnerPlugin::geMidiLearnerPlugin(int x, int y, int w, std::string l, int param, uint32_t value, ID pluginId)
-: geMidiLearnerBase(x, y, w, l, param, value),
- m_pluginId (pluginId)
-{
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geMidiLearnerPlugin::refresh()
-{
- m::model::onGet(m::model::plugins, m_pluginId, [&](const m::Plugin& p)
- {
- assert(static_cast<size_t>(m_param) < p.midiInParams.size());
- update(p.midiInParams[m_param]);
- });
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void geMidiLearnerPlugin::onLearn()
-{
- if (m_button->value() == 1)
- c::io::startPluginMidiLearn(m_param, m_pluginId);
- else
- c::io::stopMidiLearn();
-}
-
-
-void geMidiLearnerPlugin::onReset()
-{
- if (Fl::event_button() == FL_RIGHT_MOUSE)
- c::io::clearPluginMidiLearn(m_param, m_pluginId);
-}
-}} // giada::v::
-
-
-#endif
\ No newline at end of file
+++ /dev/null
-/* -----------------------------------------------------------------------------
- *
- * Giada - Your Hardcore Loopmachine
- *
- * -----------------------------------------------------------------------------
- *
- * Copyright (C) 2010-2020 Giovanni A. Zuliani | Monocasual
- *
- * This file is part of Giada - Your Hardcore Loopmachine.
- *
- * Giada - Your Hardcore Loopmachine is free software: you can
- * redistribute it and/or modify it under the terms of the GNU General
- * Public License as published by the Free Software Foundation, either
- * version 3 of the License, or (at your option) any later version.
- *
- * Giada - Your Hardcore Loopmachine is distributed in the hope that it
- * will be useful, but WITHOUT ANY WARRANTY; without even the implied
- * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Giada - Your Hardcore Loopmachine. If not, see
- * <http://www.gnu.org/licenses/>.
- *
- * -------------------------------------------------------------------------- */
-
-
-#ifdef WITH_VST
-
-
-#ifndef GE_MIDI_LEARNER_PLUGIN_H
-#define GE_MIDI_LEARNER_PLUGIN_H
-
-
-#include "core/types.h"
-#include "midiLearnerBase.h"
-
-
-namespace giada {
-namespace v
-{
-class geMidiLearnerPlugin : public geMidiLearnerBase
-{
-public:
-
- geMidiLearnerPlugin(int x, int y, int w, std::string l, int param, uint32_t value, ID pluginId);
-
- void refresh() override;
- void onLearn() override;
- void onReset() override;
-
-private:
-
- ID m_pluginId;
-};
-}} // giada::v::
-
-
-#endif
-#endif
#include <FL/fl_draw.H>
-#include "core/plugin.h"
+#include "core/plugins/plugin.h"
#include "core/const.h"
-#include "core/pluginManager.h"
-#include "core/pluginHost.h"
+#include "core/plugins/pluginManager.h"
+#include "core/plugins/pluginHost.h"
#include "gui/elems/basics/boxtypes.h"
#include "pluginBrowser.h"
add(s.c_str());
}
- for (unsigned i=0; i<m::pluginManager::countUnknownPlugins(); i++) {
+ for (int i = 0; i < m::pluginManager::countUnknownPlugins(); i++) {
std::string s = "?\t?\t?\t?\t? " + m::pluginManager::getUnknownPluginInfo(i) + " ?";
add(s.c_str());
}
void gePluginBrowser::computeWidths()
{
int w0, w1, w3;
- for (int i=0; i<m::pluginManager::countAvailablePlugins(); i++) {
+ for (int i = 0; i < m::pluginManager::countAvailablePlugins(); i++) {
m::pluginManager::PluginInfo pi = m::pluginManager::getAvailablePluginInfo(i);
w0 = (int) fl_width(pi.name.c_str());
w1 = (int) fl_width(pi.manufacturerName.c_str());
}
widths[0] += 60;
widths[1] += 60;
- widths[2] = fl_width("CATEGORY") + 60;
+ widths[2] = static_cast<int>(fl_width("CATEGORY") + 60);
widths[3] += 60;
widths[4] = 0;
}
#include <cassert>
#include <string>
-#include "core/channels/channel.h"
-#include "core/model/model.h"
#include "core/graphics.h"
-#include "core/pluginHost.h"
-#include "core/plugin.h"
+#include "core/plugins/pluginHost.h"
+#include "core/plugins/plugin.h"
#include "utils/gui.h"
#include "utils/log.h"
#include "glue/plugin.h"
namespace giada {
namespace v
{
-gePluginElement::gePluginElement(ID pluginId, ID channelId, int X, int Y, int W)
-: Fl_Pack (X, Y, W, G_GUI_UNIT),
- m_channelId(channelId),
- m_pluginId (pluginId)
+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)
{
- type(Fl_Pack::HORIZONTAL);
- spacing(G_GUI_INNER_MARGIN);
- begin();
- button = new geButton(0, 0, 196, G_GUI_UNIT);
- program = new geChoice(0, 0, 132, G_GUI_UNIT);
- bypass = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT);
- shiftUp = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", fxShiftUpOff_xpm, fxShiftUpOn_xpm);
- shiftDown = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", fxShiftDownOff_xpm, fxShiftDownOn_xpm);
- remove = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", fxRemoveOff_xpm, fxRemoveOn_xpm);
- end();
-
- m::model::PluginsLock l(m::model::plugins);
-
- const m::Plugin& p = m::model::get(m::model::plugins, m_pluginId);
-
- remove->callback(cb_removePlugin, (void*)this);
-
- if (!p.valid) {
- button->copy_label(p.getUniqueId().c_str());
- button->deactivate();
- bypass->deactivate();
- shiftUp->deactivate();
- shiftDown->deactivate();
+ 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(p.getName().c_str());
- button->callback(cb_openPluginWindow, (void*)this);
- program->callback(cb_setProgram, (void*)this);
+ button.copy_label(m_plugin.name.c_str());
+ button.callback(cb_openPluginWindow, (void*)this);
- for (int i=0; i<p.getNumPrograms(); i++)
- program->add(u::gui::removeFltkChars(p.getProgramName(i)).c_str());
+ program.callback(cb_setProgram, (void*)this);
- if (program->size() == 0) {
- program->add("-- no programs --\0");
- program->deactivate();
+ for (const auto& p : m_plugin.programs)
+ program.add(u::gui::removeFltkChars(p.name).c_str());
+
+ if (program.size() == 0) {
+ program.add("-- no programs --\0");
+ program.deactivate();
}
else
- program->value(p.getCurrentProgram());
+ program.value(m_plugin.currentProgram);
- bypass->callback(cb_setBypass, (void*)this);
- bypass->type(FL_TOGGLE_BUTTON);
- bypass->value(p.isBypassed() ? 0 : 1);
+ 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);
+ shiftUp.callback(cb_shiftUp, (void*)this);
+ shiftDown.callback(cb_shiftDown, (void*)this);
}
ID gePluginElement::getPluginId() const
{
- return m_pluginId;
+ return m_plugin.id;
}
/* -------------------------------------------------------------------------- */
-void gePluginElement::cb_removePlugin (Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_removePlugin(); }
-void gePluginElement::cb_openPluginWindow(Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_openPluginWindow(); }
-void gePluginElement::cb_setBypass (Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_setBypass(); }
-void gePluginElement::cb_shiftUp (Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_shiftUp(); }
-void gePluginElement::cb_shiftDown (Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_shiftDown(); }
-void gePluginElement::cb_setProgram (Fl_Widget* v, void* p) { ((gePluginElement*)p)->cb_setProgram(); }
+void gePluginElement::cb_removePlugin (Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_removePlugin(); }
+void gePluginElement::cb_openPluginWindow(Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_openPluginWindow(); }
+void gePluginElement::cb_setBypass (Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_setBypass(); }
+void gePluginElement::cb_shiftUp (Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_shiftUp(); }
+void gePluginElement::cb_shiftDown (Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_shiftDown(); }
+void gePluginElement::cb_setProgram (Fl_Widget* /*w*/, void* p) { ((gePluginElement*)p)->cb_setProgram(); }
/* -------------------------------------------------------------------------- */
{
const gdPluginList* parent = static_cast<const gdPluginList*>(window());
- c::plugin::swapPlugins(m_pluginId, parent->getPrevElement(*this).getPluginId(), m_channelId);
+ c::plugin::swapPlugins(m_plugin.id, parent->getPrevElement(*this).getPluginId(), m_plugin.channelId);
}
{
const gdPluginList* parent = static_cast<const gdPluginList*>(window());
- c::plugin::swapPlugins(m_pluginId, parent->getNextElement(*this).getPluginId(), m_channelId);
+ c::plugin::swapPlugins(m_plugin.id, parent->getNextElement(*this).getPluginId(), m_plugin.channelId);
}
pluginWindow has id = id_plugin + 1, because id=0 is reserved for the parent
window 'add plugin'.*/
- static_cast<gdWindow*>(window())->delSubWindow(m_pluginId + 1);
- c::plugin::freePlugin(m_pluginId, m_channelId);
+ static_cast<gdWindow*>(window())->delSubWindow(m_plugin.id + 1);
+ c::plugin::freePlugin(m_plugin.id, m_plugin.channelId);
}
void gePluginElement::cb_openPluginWindow()
{
- m::model::PluginsLock l(m::model::plugins);
-
- const m::Plugin& p = m::model::get(m::model::plugins, m_pluginId);
-
/* The new pluginWindow has id = id_plugin + 1, because id=0 is reserved for
the parent window 'add plugin'. */
- int pwid = m_pluginId + 1;
+ int pwid = m_plugin.id + 1;
gdWindow* parent = static_cast<gdWindow*>(window());
gdWindow* child = parent->getChild(pwid);
child->show(); // Raise it to top
}
else {
- if (p.hasEditor())
- child = new gdPluginWindowGUI(m_pluginId);
+ if (m_plugin.hasEditor)
+ child = new gdPluginWindowGUI(m_plugin);
else
- child = new gdPluginWindow(m_pluginId);
+ child = new gdPluginWindow(m_plugin);
child->setId(pwid);
parent->addSubWindow(child);
}
void gePluginElement::cb_setBypass()
{
- c::plugin::toggleBypass(m_pluginId);
+ c::plugin::toggleBypass(m_plugin.id);
}
void gePluginElement::cb_setProgram()
{
- c::plugin::setProgram(m_pluginId, program->value());
+ c::plugin::setProgram(m_plugin.id, program.value());
}
}} // giada::v::
#define GE_PLUGIN_ELEMENT_H
-#include <FL/Fl_Pack.H>
-
-
-class geChoice;
-class geButton;
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/button.h"
+#include "gui/elems/basics/choice.h"
+#include "glue/plugin.h"
namespace giada {
namespace v
{
-class gdPluginList;
-class gePluginElement : public Fl_Pack
+class gePluginElement : public gePack
{
public:
- gePluginElement(ID pluginId, ID channelId, int x, int y, int w);
+ gePluginElement(int x, int y, c::plugin::Plugin);
ID getPluginId() const;
- geButton* button;
- geChoice* program;
- geButton* bypass;
- geButton* shiftUp;
- geButton* shiftDown;
- geButton* remove;
+ geButton button;
+ geChoice program;
+ geButton bypass;
+ geButton shiftUp;
+ geButton shiftDown;
+ geButton remove;
private:
- static void cb_removePlugin(Fl_Widget* v, void* p);
- static void cb_openPluginWindow(Fl_Widget* v, void* p);
- static void cb_setBypass(Fl_Widget* v, void* p);
- static void cb_shiftUp(Fl_Widget* v, void* p);
- static void cb_shiftDown(Fl_Widget* v, void* p);
- static void cb_setProgram(Fl_Widget* v, void* p);
+ static void cb_removePlugin(Fl_Widget* /*w*/, void* p);
+ static void cb_openPluginWindow(Fl_Widget* /*w*/, void* p);
+ static void cb_setBypass(Fl_Widget* /*w*/, void* p);
+ static void cb_shiftUp(Fl_Widget* /*w*/, void* p);
+ static void cb_shiftDown(Fl_Widget* /*w*/, void* p);
+ static void cb_setProgram(Fl_Widget* /*w*/, void* p);
void cb_removePlugin();
void cb_openPluginWindow();
void cb_setBypass();
void cb_shiftDown();
void cb_setProgram();
- ID m_channelId;
- ID m_pluginId;
+ c::plugin::Plugin m_plugin;
};
}} // giada::v::
#ifdef WITH_VST
-#include "core/model/model.h"
-#include "core/plugin.h"
#include "core/const.h"
#include "glue/plugin.h"
+#include "glue/events.h"
#include "gui/elems/basics/boxtypes.h"
#include "gui/elems/basics/box.h"
#include "gui/elems/basics/slider.h"
namespace giada {
namespace v
{
-gePluginParameter::gePluginParameter(int paramIndex, ID pluginId,
- int X, int Y, int W, int labelWidth)
-: Fl_Group (X, Y, W, G_GUI_UNIT),
- m_pluginId (pluginId),
- m_paramIndex(paramIndex)
+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)
{
- m::model::PluginsLock l(m::model::plugins);
- const m::Plugin& p = m::model::get(m::model::plugins, m_pluginId);
-
begin();
const int VALUE_WIDTH = 100;
m_label = new geBox(x(), y(), labelWidth, G_GUI_UNIT);
- m_label->copy_label(p.getParameterName(m_paramIndex).c_str());
- m_label->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
+ 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(p.getParameter(m_paramIndex));
+ 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(false);
+ update(m_param, false);
}
/* -------------------------------------------------------------------------- */
-void gePluginParameter::cb_setValue(Fl_Widget* v, void* p) { ((gePluginParameter*)p)->cb_setValue(); }
+void gePluginParameter::cb_setValue(Fl_Widget* /*w*/, void* p) { ((gePluginParameter*)p)->cb_setValue(); }
/* -------------------------------------------------------------------------- */
void gePluginParameter::cb_setValue()
{
- c::plugin::setParameter(m_pluginId, m_paramIndex, m_slider->value(),
+ c::events::setPluginParameter(m_param.pluginId, m_param.index, m_slider->value(),
/*gui=*/true);
}
/* -------------------------------------------------------------------------- */
-void gePluginParameter::update(bool changeSlider)
+void gePluginParameter::update(const c::plugin::Param& p, bool changeSlider)
{
- m::model::PluginsLock l(m::model::plugins);
- const m::Plugin& p = m::model::get(m::model::plugins, m_pluginId);
-
- std::string v = p.getParameterText(m_paramIndex) + " " +
- p.getParameterLabel(m_paramIndex);
-
- m_value->copy_label(v.c_str());
-
+ m_value->copy_label(std::string(p.text + " " + p.label).c_str());
if (changeSlider)
- m_slider->value(p.getParameter(m_paramIndex));
+ m_slider->value(p.value);
}
}} // giada::v::
namespace giada {
+namespace c {
+namespace plugin
+{
+struct Param;
+}}
namespace v
{
class gePluginParameter : public Fl_Group
{
public:
- gePluginParameter(int paramIndex, ID pluginId, int x, int y, int w,
- int labelWidth);
+ gePluginParameter(int x, int y, int w, int labelWidth, const c::plugin::Param);
- void update(bool changeSlider);
+ void update(const c::plugin::Param& p, bool changeSlider);
private:
- static void cb_setValue(Fl_Widget* v, void* p);
+ static void cb_setValue(Fl_Widget* /*w*/, void* p);
void cb_setValue();
- ID m_pluginId;
- int m_paramIndex;
+ const c::plugin::Param m_param;
geBox* m_label;
geSlider* m_slider;
#include <FL/Fl.H>
-#include "core/channels/sampleChannel.h"
#include "core/const.h"
#include "core/waveFx.h"
#include "glue/channel.h"
/* -------------------------------------------------------------------------- */
-void geBoostTool::cb_setBoost (Fl_Widget* w, void* p) { ((geBoostTool*)p)->cb_setBoost(); }
-void geBoostTool::cb_setBoostNum(Fl_Widget* w, void* p) { ((geBoostTool*)p)->cb_setBoostNum(); }
-void geBoostTool::cb_normalize (Fl_Widget* w, void* p) { ((geBoostTool*)p)->cb_normalize(); }
+void geBoostTool::cb_setBoost (Fl_Widget* /*w*/, void* p) { ((geBoostTool*)p)->cb_setBoost(); }
+void geBoostTool::cb_setBoostNum(Fl_Widget* /*w*/, void* p) { ((geBoostTool*)p)->cb_setBoostNum(); }
+void geBoostTool::cb_normalize (Fl_Widget* /*w*/, void* p) { ((geBoostTool*)p)->cb_normalize(); }
/* -------------------------------------------------------------------------- */
private:
- static void cb_setBoost(Fl_Widget* w, void* p);
- static void cb_setBoostNum(Fl_Widget* w, void* p);
- static void cb_normalize(Fl_Widget* w, void* p);
+ static void cb_setBoost(Fl_Widget* /*w*/, void* p);
+ static void cb_setBoostNum(Fl_Widget* /*w*/, void* p);
+ static void cb_normalize(Fl_Widget* /*w*/, void* p);
void cb_setBoost();
void cb_setBoostNum();
void cb_normalize();
#include <FL/Fl.H>
-#include "core/channels/sampleChannel.h"
#include "core/model/model.h"
#include "core/const.h"
#include "core/waveFx.h"
-#include "glue/channel.h"
+#include "glue/events.h"
#include "utils/gui.h"
#include "utils/math.h"
#include "utils/string.h"
#include "gui/dialogs/sampleEditor.h"
-#include "gui/elems/basics/dial.h"
-#include "gui/elems/basics/input.h"
-#include "gui/elems/basics/box.h"
-#include "gui/elems/basics/button.h"
#include "waveTools.h"
#include "panTool.h"
namespace giada {
namespace v
{
-gePanTool::gePanTool(ID channelId, int x, int y)
-: Fl_Pack (x, y, 200, G_GUI_UNIT),
- m_channelId(channelId)
+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, "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, "Reset")
{
- type(Fl_Pack::HORIZONTAL);
- spacing(G_GUI_INNER_MARGIN);
+ add(&m_label);
+ add(&m_dial);
+ add(&m_input);
+ add(&m_reset);
- begin();
- label = new geBox (0, 0, u::gui::getStringWidth("Pan"), G_GUI_UNIT, "Pan", FL_ALIGN_RIGHT);
- dial = new geDial (0, 0, G_GUI_UNIT, G_GUI_UNIT);
- input = new geInput (0, 0, 70, G_GUI_UNIT);
- reset = new geButton(0, 0, 70, G_GUI_UNIT, "Reset");
- end();
+ m_dial.range(0.0f, G_MAX_PAN);
+ m_dial.callback(cb_panning, (void*)this);
- dial->range(0.0f, 1.0f);
- dial->callback(cb_panning, (void*)this);
+ m_input.align(FL_ALIGN_RIGHT);
+ m_input.readonly(1);
+ m_input.cursor_color(FL_WHITE);
- input->align(FL_ALIGN_RIGHT);
- input->readonly(1);
- input->cursor_color(FL_WHITE);
+ m_reset.callback(cb_panReset, (void*)this);
- reset->callback(cb_panReset, (void*)this);
+ rebuild(d);
}
/* -------------------------------------------------------------------------- */
-void gePanTool::rebuild()
+void gePanTool::rebuild(const c::sampleEditor::Data& d)
{
- float p;
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- p = static_cast<m::SampleChannel&>(c).getPan();
- });
+ m_data = &d;
+ update(m_data->pan);
- dial->value(p);
+}
+
+
+/* -------------------------------------------------------------------------- */
- if (p < 0.5f) {
- std::string tmp = u::string::iToString((int) ((-p * 200.0f) + 100.0f)) + " L";
- input->value(tmp.c_str());
+
+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 (p == 0.5)
- input->value("C");
+ if (v == 0.5)
+ m_input.value("C");
else {
- std::string tmp = u::string::iToString((int) ((p * 200.0f) - 100.0f)) + " R";
- input->value(tmp.c_str());
+ 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 (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::channel::setPan(m_channelId, dial->value());
+ c::events::sendChannelPan(m_data->channelId, m_dial.value());
}
void gePanTool::cb_panReset()
{
- c::channel::setPan(m_channelId, 0.5f);
+ c::events::sendChannelPan(m_data->channelId, 0.5f);
}
-
}} // giada::v::
#define GE_PAN_TOOL_H
-#include <FL/Fl_Pack.H>
-
-
-class geDial;
-class geInput;
-class geButton;
-class geBox;
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/dial.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/button.h"
namespace giada {
namespace v
{
-class gePanTool : public Fl_Pack
+class gePanTool : public gePack
{
public:
- gePanTool(ID channelId, int x, int y);
+ gePanTool(const c::sampleEditor::Data& d, int x, int y);
- void rebuild();
+ 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();
-
- ID m_channelId;
+ static void cb_panning (Fl_Widget* /*w*/, void* p);
+ static void cb_panReset(Fl_Widget* /*w*/, void* p);
+ void cb_panning();
+ void cb_panReset();
- geBox* label;
- geDial* dial;
- geInput* input;
- geButton* reset;
+ const c::sampleEditor::Data* m_data;
+ geBox m_label;
+ geDial m_dial;
+ geInput m_input;
+ geButton m_reset;
};
}} // giada::v::
#include <FL/Fl.H>
-#include "core/channels/sampleChannel.h"
#include "core/model/model.h"
#include "core/const.h"
#include "core/graphics.h"
#include "core/clock.h"
-#include "glue/channel.h"
+#include "glue/events.h"
#include "utils/gui.h"
#include "utils/string.h"
#include "gui/dialogs/sampleEditor.h"
namespace giada {
namespace v
{
-gePitchTool::gePitchTool(ID channelId, int x, int y)
-: Fl_Pack (x, y, 600, G_GUI_UNIT),
- m_channelId(channelId)
+gePitchTool::gePitchTool(const c::sampleEditor::Data& d, int x, int y)
+: gePack (x, y, Direction::HORIZONTAL)
+, m_data (nullptr)
+, m_label (0, 0, 60, G_GUI_UNIT, "Pitch", FL_ALIGN_LEFT)
+, m_dial (0, 0, G_GUI_UNIT, G_GUI_UNIT)
+, m_input (0, 0, 70, G_GUI_UNIT)
+, m_pitchToBar (0, 0, 70, G_GUI_UNIT, "To bar")
+, m_pitchToSong(0, 0, 70, G_GUI_UNIT, "To song")
+, m_pitchHalf (0, 0, G_GUI_UNIT, G_GUI_UNIT, "", divideOff_xpm, divideOn_xpm)
+, m_pitchDouble(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", multiplyOff_xpm, multiplyOn_xpm)
+, m_pitchReset (0, 0, 70, G_GUI_UNIT, "Reset")
{
- type(Fl_Pack::HORIZONTAL);
- spacing(G_GUI_INNER_MARGIN);
-
- begin();
- label = new geBox (0, 0, u::gui::getStringWidth("Pitch"), G_GUI_UNIT, "Pitch", FL_ALIGN_RIGHT);
- dial = new geDial (0, 0, G_GUI_UNIT, G_GUI_UNIT);
- input = new geInput (0, 0, 70, G_GUI_UNIT);
- pitchToBar = new geButton(0, 0, 70, G_GUI_UNIT, "To bar");
- pitchToSong = new geButton(0, 0, 70, G_GUI_UNIT, "To song");
- pitchHalf = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", divideOff_xpm, divideOn_xpm);
- pitchDouble = new geButton(0, 0, G_GUI_UNIT, G_GUI_UNIT, "", multiplyOff_xpm, multiplyOn_xpm);
- pitchReset = new geButton(0, 0, 70, G_GUI_UNIT, "Reset");
- end();
-
- dial->range(0.01f, 4.0f);
- dial->callback(cb_setPitch, (void*)this);
- dial->when(FL_WHEN_RELEASE);
-
- input->align(FL_ALIGN_RIGHT);
- input->callback(cb_setPitchNum, (void*)this);
- input->when(FL_WHEN_RELEASE | FL_WHEN_ENTER_KEY);
-
- pitchToBar->callback(cb_setPitchToBar, (void*)this);
- pitchToSong->callback(cb_setPitchToSong, (void*)this);
- pitchHalf->callback(cb_setPitchHalf, (void*)this);
- pitchDouble->callback(cb_setPitchDouble, (void*)this);
- pitchReset->callback(cb_resetPitch, (void*)this);
+ 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()
+void gePitchTool::rebuild(const c::sampleEditor::Data& d)
{
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- float p = static_cast<m::SampleChannel&>(c).getPitch();
-
- dial->value(p);
- input->value(u::string::fToString(p, 4).c_str()); // 4 digits
- });
+ m_data = &d;
+ update(m_data->pitch, /*isDial=*/false);
}
/* -------------------------------------------------------------------------- */
-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::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::channel::setPitch(m_channelId, dial->value());
+ c::events::setChannelPitch(m_data->channelId, m_dial.value(), Thread::MAIN);
}
void gePitchTool::cb_setPitchNum()
{
- c::channel::setPitch(m_channelId, atof(input->value()));
+ c::events::setChannelPitch(m_data->channelId, atof(m_input.value()), Thread::MAIN);
}
void gePitchTool::cb_setPitchHalf()
{
- c::channel::setPitch(m_channelId, dial->value()/2);
+ c::events::setChannelPitch(m_data->channelId, m_dial.value() / 2, Thread::MAIN);
}
void gePitchTool::cb_setPitchDouble()
{
- c::channel::setPitch(m_channelId, dial->value()*2);
+ c::events::setChannelPitch(m_data->channelId, m_dial.value() * 2, Thread::MAIN);
}
void gePitchTool::cb_setPitchToBar()
{
- Frame end;
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- end = static_cast<m::SampleChannel&>(c).getEnd();
- });
-
- c::channel::setPitch(m_channelId, end / (float) m::clock::getFramesInBar());
+ c::events::setChannelPitch(m_data->channelId, m_data->end / (float) m::clock::getFramesInBar(),
+ Thread::MAIN);
}
void gePitchTool::cb_setPitchToSong()
{
- Frame end;
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- end = static_cast<m::SampleChannel&>(c).getEnd();
- });
-
- c::channel::setPitch(m_channelId, end / (float) m::clock::getFramesInLoop());
+ c::events::setChannelPitch(m_data->channelId, m_data->end / (float) m::clock::getFramesInLoop(),
+ Thread::MAIN);
}
void gePitchTool::cb_resetPitch()
{
- c::channel::setPitch(m_channelId, G_DEFAULT_PITCH);
+ c::events::setChannelPitch(m_data->channelId, G_DEFAULT_PITCH, Thread::MAIN);
}
-
}} // giada::v::
#define GE_PITCH_TOOL_H
-#include <FL/Fl_Pack.H>
-
-
-class geDial;
-class geInput;
-class geButton;
-class geBox;
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/dial.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/button.h"
namespace giada {
namespace v
{
-class gePitchTool : public Fl_Pack
+class gePitchTool : public gePack
{
public:
- gePitchTool(ID channelId, int x, int y);
+ gePitchTool(const c::sampleEditor::Data& d, int x, int y);
- void rebuild();
+ 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);
+ 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_resetPitch();
void cb_setPitchNum();
- ID m_channelId;
+ const c::sampleEditor::Data* m_data;
- geBox* label;
- geDial* dial;
- geInput* input;
- geButton* pitchToBar;
- geButton* pitchToSong;
- geButton* pitchHalf;
- geButton* pitchDouble;
- geButton* pitchReset;
+ geBox m_label;
+ geDial m_dial;
+ geInput m_input;
+ geButton m_pitchToBar;
+ geButton m_pitchToSong;
+ geButton m_pitchHalf;
+ geButton m_pitchDouble;
+ geButton m_pitchReset;
};
}} // giada::v::
#include <cassert>
#include <FL/Fl.H>
-#include "core/channels/sampleChannel.h"
#include "core/model/model.h"
#include "core/wave.h"
#include "glue/channel.h"
#include "utils/gui.h"
#include "utils/string.h"
#include "gui/dialogs/sampleEditor.h"
-#include "gui/elems/basics/input.h"
-#include "gui/elems/basics/box.h"
-#include "gui/elems/basics/button.h"
#include "waveTools.h"
#include "rangeTool.h"
namespace giada {
namespace v
{
-geRangeTool::geRangeTool(ID channelId, ID waveId, int x, int y)
-: Fl_Pack (x, y, 280, G_GUI_UNIT),
- m_channelId(channelId),
- m_waveId (waveId)
+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, "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, "Reset")
{
- type(Fl_Pack::HORIZONTAL);
- spacing(G_GUI_INNER_MARGIN);
-
- begin();
- m_label = new geBox (0, 0, u::gui::getStringWidth("Range"), G_GUI_UNIT, "Range", FL_ALIGN_RIGHT);
- m_begin = new geInput (0, 0, 70, G_GUI_UNIT);
- m_end = new geInput (0, 0, 70, G_GUI_UNIT);
- m_reset = new geButton(0, 0, 70, G_GUI_UNIT, "Reset");
- end();
-
- 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);
+ 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_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);
+ m_reset.callback(cb_resetStartEnd, this);
+
+ rebuild(d);
}
/* -------------------------------------------------------------------------- */
-void geRangeTool::rebuild()
+void geRangeTool::rebuild(const c::sampleEditor::Data& d)
{
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- m_begin->value(std::to_string(static_cast<m::SampleChannel&>(c).getBegin()).c_str());
- m_end->value(std::to_string(static_cast<m::SampleChannel&>(c).getEnd()).c_str());
- });
+ m_data = &d;
+ update(m_data->begin, m_data->end);
}
/* -------------------------------------------------------------------------- */
-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::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_channelId, atoi(m_begin->value()), atoi(m_end->value()));
+ c::sampleEditor::setBeginEnd(m_data->channelId, atoi(m_begin.value()), atoi(m_end.value()));
}
void geRangeTool::cb_resetStartEnd()
{
- Frame waveSize;
- m::model::onGet(m::model::waves, m_waveId, [&](m::Wave& w)
- {
- waveSize = w.getSize();
- });
-
- c::sampleEditor::setBeginEnd(m_channelId, 0, waveSize - 1);
+ c::sampleEditor::setBeginEnd(m_data->channelId, 0, m_data->waveSize - 1);
}
}} // giada::v::
#define GE_RANGE_TOOL_H
-#include <FL/Fl_Pack.H>
-
-
-class geInput;
-class geButton;
-class geBox;
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/button.h"
namespace giada {
namespace v
{
-class geRangeTool : public Fl_Pack
+class geRangeTool : public gePack
{
public:
- geRangeTool(ID channelId, ID waveId, int x, int y);
+ geRangeTool(const c::sampleEditor::Data& d, int x, int y);
- void rebuild();
+ 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);
+ static void cb_setChanPos (Fl_Widget* /*w*/, void* p);
+ static void cb_resetStartEnd(Fl_Widget* /*w*/, void* p);
void cb_setChanPos();
void cb_resetStartEnd();
- ID m_channelId;
- ID m_waveId;
+ const c::sampleEditor::Data* m_data;
- geBox* m_label;
- geInput* m_begin;
- geInput* m_end;
- geButton* m_reset;
+ geBox m_label;
+ geInput m_begin;
+ geInput m_end;
+ geButton m_reset;
};
}} // giada::v::
#include <cassert>
#include <cstdlib>
-#include "core/channels/sampleChannel.h"
#include "core/model/model.h"
#include "core/const.h"
#include "utils/gui.h"
#include "glue/sampleEditor.h"
#include "gui/dialogs/warnings.h"
#include "gui/dialogs/sampleEditor.h"
-#include "gui/elems/basics/input.h"
-#include "gui/elems/basics/box.h"
-#include "gui/elems/basics/button.h"
#include "shiftTool.h"
namespace giada {
namespace v
{
-geShiftTool::geShiftTool(ID channelId, ID waveId, int x, int y)
-: Fl_Pack (x, y, 300, G_GUI_UNIT),
- m_channelId(channelId),
- m_waveId (waveId)
+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, "Shift", FL_ALIGN_LEFT)
+, m_shift(0, 0, 70, G_GUI_UNIT)
+, m_reset(0, 0, 70, G_GUI_UNIT, "Reset")
{
- type(Fl_Pack::HORIZONTAL);
- spacing(G_GUI_INNER_MARGIN);
+ add(&m_label);
+ add(&m_shift);
+ add(&m_reset);
- begin();
- m_label = new geBox (0, 0, u::gui::getStringWidth("Shift"), G_GUI_UNIT, "Shift", FL_ALIGN_RIGHT);
- m_shift = new geInput (0, 0, 70, G_GUI_UNIT);
- m_reset = new geButton(0, 0, 70, G_GUI_UNIT, "Reset");
- end();
+ 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_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);
- 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(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()));
+ shift(atoi(m_shift.value()));
}
/* -------------------------------------------------------------------------- */
-void geShiftTool::rebuild()
+void geShiftTool::rebuild(const c::sampleEditor::Data& d)
{
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- m_shift->value(std::to_string(static_cast<m::SampleChannel&>(c).shift).c_str());
- });
+ 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_channelId, m_waveId, f);
+ c::sampleEditor::shift(m_data->channelId, m_data->waveId, f);
}
}} // giada::v::
#define GE_SHIFT_TOOL_H
-#include <FL/Fl_Pack.H>
-
-
-class geInput;
-class geButton;
-class geBox;
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/input.h"
+#include "gui/elems/basics/button.h"
namespace giada {
namespace v
{
-class geShiftTool : public Fl_Pack
+class geShiftTool : public gePack
{
public:
- geShiftTool(ID channelId, ID waveId, int x, int y);
+ geShiftTool(const c::sampleEditor::Data& d, int x, int y);
- void rebuild();
+ 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);
+ 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);
- ID m_channelId;
- ID m_waveId;
+ const c::sampleEditor::Data* m_data;
- geBox* m_label;
- geInput* m_shift;
- geButton* m_reset;
+ geBox m_label;
+ geInput m_shift;
+ geButton m_reset;
};
}} // giada::v::
#include <cmath>
#include <cstdlib>
#include <FL/Fl_Pack.H>
-#include "core/channels/sampleChannel.h"
#include "core/const.h"
-#include "core/model/model.h"
-#include "glue/channel.h"
+#include "glue/events.h"
#include "utils/gui.h"
#include "utils/math.h"
#include "utils/string.h"
#include "gui/dialogs/sampleEditor.h"
-#include "gui/elems/basics/dial.h"
-#include "gui/elems/basics/input.h"
-#include "gui/elems/basics/box.h"
#include "gui/elems/mainWindow/keyboard/channel.h"
#include "volumeTool.h"
namespace giada {
namespace v
{
-geVolumeTool::geVolumeTool(ID channelId, int X, int Y)
-: Fl_Pack (X, Y, 150, G_GUI_UNIT),
- m_channelId(channelId)
-{
- type(Fl_Pack::HORIZONTAL);
- spacing(G_GUI_INNER_MARGIN);
+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, "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);
+}
- begin();
- label = new geBox (0, 0, u::gui::getStringWidth("Volume"), G_GUI_UNIT, "Volume", FL_ALIGN_RIGHT);
- dial = new geDial (0, 0, G_GUI_UNIT, G_GUI_UNIT);
- input = new geInput(0, 0, 70, G_GUI_UNIT);
- end();
- dial->range(0.0f, 1.0f);
- dial->callback(cb_setVolume, (void*)this);
+/* -------------------------------------------------------------------------- */
+
- input->callback(cb_setVolumeNum, (void*)this);
+void geVolumeTool::rebuild(const c::sampleEditor::Data& d)
+{
+ m_data = &d;
+ update(m_data->volume, /*isDial=*/false);
}
/* -------------------------------------------------------------------------- */
-void geVolumeTool::rebuild()
+void geVolumeTool::update(float v, bool isDial)
{
- float volume;
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c) { volume = c.volume; });
-
std::string tmp = "-inf";
- float dB = u::math::linearToDB(volume);
+ float dB = u::math::linearToDB(v);
if (dB > -INFINITY)
tmp = u::string::fToString(dB, 2); // 2 digits
- input->value(tmp.c_str());
- dial->value(volume);
+ 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 (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::channel::setVolume(m_channelId, dial->value(), false, true);
+ c::events::setChannelVolume(m_data->channelId, m_dial.value(), Thread::MAIN);
}
void geVolumeTool::cb_setVolumeNum()
{
- float value = pow(10, (atof(input->value()) / 20)); // linear = 10^(dB/20)
- c::channel::setVolume(m_channelId, value, false, true);
+ c::events::setChannelVolume(m_data->channelId, u::math::dBtoLinear(atof(m_input.value())),
+ Thread::MAIN);
}
-
}} // giada::v::
#define GE_VOLUME_TOOL_H
-#include <FL/Fl_Pack.H>
-
-
-class geDial;
-class geInput;
-class geBox;
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/box.h"
+#include "gui/elems/basics/dial.h"
+#include "gui/elems/basics/input.h"
namespace giada {
namespace v
{
-class geVolumeTool : public Fl_Pack
+class geVolumeTool : public gePack
{
public:
- geVolumeTool(ID channelId, int x, int y);
+ geVolumeTool(const c::sampleEditor::Data& d, int x, int y);
- void rebuild();
+ void rebuild(const c::sampleEditor::Data& d);
+ void update(float v, bool isDial=false);
private:
- ID m_channelId;
+ static void cb_setVolume(Fl_Widget* /*w*/, void* p);
+ static void cb_setVolumeNum(Fl_Widget* /*w*/, void* p);
+ void cb_setVolume();
+ void cb_setVolumeNum();
- geBox* label;
- geDial* dial;
- geInput* input;
+ const c::sampleEditor::Data* m_data;
+
+ geBox m_label;
+ geDial m_dial;
+ geInput m_input;
- static void cb_setVolume (Fl_Widget* w, void* p);
- static void cb_setVolumeNum(Fl_Widget* w, void* p);
- void cb_setVolume ();
- void cb_setVolumeNum();
};
}} // giada::v::
#include <cstdint>
#include <FL/Fl_Menu_Item.H>
#include <FL/Fl_Menu_Button.H>
-#include "core/channels/sampleChannel.h"
#include "core/model/model.h"
#include "core/waveFx.h"
#include "core/const.h"
{
const geWaveTools* wt = static_cast<geWaveTools*>(w);
- ID channelId = wt->channelId;
- size_t waveId = wt->waveId;
- Menu selectedItem = (Menu) (intptr_t) v;
+ ID channelId = wt->getChannelData().channelId;
+ ID waveId = wt->getChannelData().waveId;
+ Menu selectedItem = (Menu) (intptr_t) v;
int a = wt->waveform->getSelectionA();
int b = wt->waveform->getSelectionB();
c::sampleEditor::trim(channelId, waveId, a, b);
break;
case Menu::SILENCE:
- c::sampleEditor::silence(waveId, a, b);
+ c::sampleEditor::silence(channelId, waveId, a, b);
break;
case Menu::REVERSE:
- c::sampleEditor::reverse(waveId, a, b);
+ c::sampleEditor::reverse(channelId, waveId, a, b);
break;
case Menu::NORMALIZE:
- c::sampleEditor::normalizeHard(waveId, a, b);
+ c::sampleEditor::normalize(channelId, waveId, a, b);
break;
case Menu::FADE_IN:
- c::sampleEditor::fade(waveId, a, b, m::wfx::FADE_IN);
+ c::sampleEditor::fade(channelId, waveId, a, b, m::wfx::Fade::IN);
break;
case Menu::FADE_OUT:
- c::sampleEditor::fade(waveId, a, b, m::wfx::FADE_OUT);
+ c::sampleEditor::fade(channelId, waveId, a, b, m::wfx::Fade::OUT);
break;
case Menu::SMOOTH_EDGES:
- c::sampleEditor::smoothEdges(waveId, a, b);
+ c::sampleEditor::smoothEdges(channelId, waveId, a, b);
break;
case Menu::SET_BEGIN_END:
- c::sampleEditor::setBeginEnd(waveId, a, b);
+ c::sampleEditor::setBeginEnd(channelId, a, b);
break;
case Menu::TO_NEW_CHANNEL:
- c::sampleEditor::toNewChannel(waveId, a, b);
+ c::sampleEditor::toNewChannel(channelId, waveId, a, b);
break;
}
}
-}; // {anonymous}
+} // {anonymous}
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-geWaveTools::geWaveTools(ID channelId, ID waveId, int x, int y, int w, int h)
-: Fl_Scroll(x, y, w, h, nullptr),
- channelId(channelId),
- waveId(waveId)
+geWaveTools::geWaveTools(int x, int y, int w, int h)
+: Fl_Scroll(x, y, w, h, nullptr)
+, m_data (nullptr)
{
type(Fl_Scroll::HORIZONTAL_ALWAYS);
hscrollbar.color(G_COLOR_GREY_2);
hscrollbar.labelcolor(G_COLOR_LIGHT_1);
hscrollbar.slider(G_CUSTOM_BORDER_BOX);
- waveform = new v::geWaveform(channelId, waveId, x, y, w, h-24);
+ waveform = new v::geWaveform(x, y, w, h-24);
}
/* -------------------------------------------------------------------------- */
-void geWaveTools::rebuild()
+void geWaveTools::rebuild(const c::sampleEditor::Data& d)
{
- waveform->rebuild();
+ m_data = &d;
+ waveform->rebuild(d);
}
void geWaveTools::refresh()
{
- m::model::onGet(m::model::channels, channelId, [&](m::Channel& c)
- {
- if (c.isPreview())
- waveform->redraw();
- });
+ if (m_data->a_getPreviewStatus() == ChannelStatus::PLAY)
+ waveform->redraw();
}
if (this->w() == w || (this->w() != w && this->h() != h)) { // vertical or both resize
waveform->resize(x, y, waveform->w(), h-24);
- waveform->rebuild();
+ waveform->rebuild(*m_data);
}
if (this->w() > waveform->w())
{
public:
- geWaveTools(ID channelId, ID waveId, int x, int y, int w, int h);
+ geWaveTools(int x, int y, int w, int h);
void resize(int x, int y, int w, int h) override;
int handle(int e) override;
Updates the waveform by realloc-ing new data (i.e. when the waveform has
changed). */
- void rebuild();
+ void rebuild(const c::sampleEditor::Data& d);
/* refresh
Redraws the waveform, called by the video thread. This is meant to be called
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;
- ID channelId;
- ID waveId;
-
private:
void openMenu();
+
+ const c::sampleEditor::Data* m_data;
};
}} // giada::v::
#include <cmath>
#include <FL/fl_draw.H>
#include <FL/Fl_Menu_Button.H>
-#include "core/channels/sampleChannel.h"
#include "core/model/model.h"
#include "core/wave.h"
#include "core/conf.h"
namespace giada {
namespace v
{
-geWaveform::geWaveform(ID channelId, ID waveId, int x, int y, int w, int h)
-: Fl_Widget (x, y, w, h, nullptr),
- m_selection {},
- m_channelId (channelId),
- m_waveId (waveId),
- 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)
+geWaveform::geWaveform(int x, int y, int w, int h)
+: Fl_Widget (x, y, w, h, nullptr)
+, m_selection {}
+, m_data (nullptr)
+, m_chanStart (0)
+, 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_data.size = w;
+ m_waveform.size = w;
m_grid.snap = m::conf::conf.sampleEditorGridOn;
m_grid.level = m::conf::conf.sampleEditorGridVal;
void geWaveform::clearData()
{
- m_data.sup.clear();
- m_data.inf.clear();
- m_data.size = 0;
+ m_waveform.sup.clear();
+ m_waveform.inf.clear();
+ m_waveform.size = 0;
m_grid.points.clear();
}
int geWaveform::alloc(int datasize, bool force)
{
- m::model::WavesLock lock(m::model::waves);
+ /* TODO - geWaveform needs better isolation from m::. Refactoring needed. */
- const m::Wave& wave = m::model::get(m::model::waves, m_waveId);
+ m::model::WavesLock l(m::model::waves);
+ const m::Wave& wave = m::model::get(m::model::waves, m_data->waveId);
m_ratio = wave.getSize() / (float) datasize;
m_ratio = 1;
}
- if (datasize == m_data.size && !force)
+ if (datasize == m_waveform.size && !force)
return 0;
clearData();
- m_data.size = datasize;
- m_data.sup.resize(m_data.size);
- m_data.inf.resize(m_data.size);
+ 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_data.size, m_ratio);
+ 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)
/* Resampling the waveform, hardcore way. Many thanks to
http://fourier.eng.hmc.edu/e161/lectures/resize/node3.html */
- for (int i = 0; i < m_data.size; i++) {
+ for (int i = 0; i < m_waveform.size; i++) {
/* Scan the original waveform in chunks [pc, pn]. */
m_grid.points.push_back(k);
}
- m_data.sup[i] = zero - (peaksup * offset);
- m_data.inf[i] = zero - (peakinf * offset);
+ m_waveform.sup[i] = zero - (peaksup * offset);
+ m_waveform.inf[i] = zero - (peakinf * offset);
// avoid window overflow
- if (m_data.sup[i] < y()) m_data.sup[i] = y();
- if (m_data.inf[i] > y()+h()-1) m_data.inf[i] = y()+h()-1;
+ if (m_waveform.sup[i] < y()) m_waveform.sup[i] = y();
+ if (m_waveform.inf[i] > y()+h()-1) m_waveform.inf[i] = y()+h()-1;
}
recalcPoints();
void geWaveform::recalcPoints()
{
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- m_chanStart = static_cast<m::SampleChannel&>(c).getBegin();
- m_chanEnd = static_cast<m::SampleChannel&>(c).getEnd();
- });
+ m_chanStart = m_data->begin;
+ m_chanEnd = m_data->end;
}
fl_color(G_COLOR_BLACK);
for (int i=from; i<to; i++) {
- if (i >= m_data.size)
+ if (i >= m_waveform.size)
break;
- fl_line(i+x(), zero, i+x(), m_data.sup[i]);
- fl_line(i+x(), zero, i+x(), m_data.inf[i]);
+ fl_line(i+x(), zero, i+x(), m_waveform.sup[i]);
+ fl_line(i+x(), zero, i+x(), m_waveform.inf[i]);
}
}
void geWaveform::drawPlayHead()
{
- float tp;
- m::model::onGet(m::model::channels, m_channelId, [&](m::Channel& c)
- {
- tp = static_cast<m::SampleChannel&>(c).trackerPreview;
- });
-
- int p = frameToPixel(tp) + x();
+ 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_data.sup.size() > 0);
- assert(m_data.inf.size() > 0);
+ assert(m_waveform.sup.size() > 0);
+ assert(m_waveform.inf.size() > 0);
fl_rectf(x(), y(), w(), h(), G_COLOR_GREY_2); // blank canvas
int geWaveform::handle(int e)
{
- m::model::WavesLock lock(m::model::waves);
-
- const m::Wave& wave = m::model::get(m::model::waves, m_waveId);
+ /* TODO - geWaveform needs better isolation from m::. Refactoring needed. */
+
+ m::model::WavesLock l(m::model::waves);
+ const m::Wave& wave = m::model::get(m::model::waves, m_data->waveId);
m_mouseX = pixelToFrame(Fl::event_x() - x());
m_mouseY = pixelToFrame(Fl::event_y() - y());
static_cast<v::gdSampleEditor*>(window())->cb_togglePreview();
else
if (Fl::event_key() == FL_BackSpace)
- c::sampleEditor::rewindPreview(m_channelId);
+ c::sampleEditor::setPreviewTracker(m_data->begin);
return 1;
}
case FL_RELEASE: {
- c::sampleEditor::setPlayHead(m_channelId, m_mouseX);
+ c::sampleEditor::setPreviewTracker(m_mouseX);
/* If selection has been done (m_dragged or resized), make sure that point A
is always lower than B. */
/* Handle begin/end markers interaction. */
if (m_chanStartLit || m_chanEndLit)
- c::sampleEditor::setBeginEnd(m_channelId, m_chanStart, m_chanEnd);
+ c::sampleEditor::setBeginEnd(m_data->channelId, m_chanStart, m_chanEnd);
m_pushed = false;
m_dragged = false;
int geWaveform::pixelToFrame(int p) const
{
- Frame waveSize;
- m::model::onGet(m::model::waves, m_waveId, [&](m::Wave& w) { waveSize = w.getSize(); });
-
if (p <= 0)
return 0;
- if (p > m_data.size)
- return waveSize - 1;
+ if (p > m_waveform.size)
+ return m_data->waveSize - 1;
return p * m_ratio;
}
if (m_selection.a > m_selection.b) // inverted m_selection
std::swap(m_selection.a, m_selection.b);
- c::sampleEditor::setPlayHead(m_channelId, m_selection.a);
+ c::sampleEditor::setPreviewTracker(m_selection.a);
}
void geWaveform::setZoom(Zoom z)
{
- if (!alloc(z == Zoom::IN ? m_data.size * G_GUI_ZOOM_FACTOR : m_data.size / G_GUI_ZOOM_FACTOR))
+ if (!alloc(z == Zoom::IN ? m_waveform.size * G_GUI_ZOOM_FACTOR : m_waveform.size / G_GUI_ZOOM_FACTOR))
return;
- size(m_data.size, h());
+ size(m_waveform.size, h());
/* Zoom to cursor. */
/* -------------------------------------------------------------------------- */
-void geWaveform::rebuild()
+void geWaveform::rebuild(const c::sampleEditor::Data& d)
{
+ m_data = &d;
clearSelection();
- alloc(m_data.size, /*force=*/true);
+ alloc(m_waveform.size, /*force=*/true);
redraw();
}
{
m_grid.points.clear();
m_grid.level = l;
- alloc(m_data.size, true); // force alloc
+ alloc(m_waveform.size, true); // force alloc
redraw();
}
void geWaveform::setSnap(bool v) { m_grid.snap = v; }
bool geWaveform::getSnap() const { return m_grid.snap; }
-int geWaveform::getSize() const { return m_data.size; }
+int geWaveform::getSize() const { return m_waveform.size; }
/* -------------------------------------------------------------------------- */
void geWaveform::selectAll()
{
- Frame waveSize;
- m::model::onGet(m::model::waves, m_waveId, [&](m::Wave& w) { waveSize = w.getSize(); });
-
m_selection.a = 0;
- m_selection.b = waveSize - 1;
+ m_selection.b = m_data->waveSize - 1;
redraw();
}
}} // giada::v::
#endif
enum class Zoom { IN, OUT };
- geWaveform(ID channelId, ID waveId, int x, int y, int w, int h);
+ geWaveform(int x, int y, int w, int h);
void draw() override;
int handle(int e) override;
/* rebuild
Redraws the waveform. */
- void rebuild();
+ void rebuild(const c::sampleEditor::Data& d);
/* setGridLevel
Sets a new frequency level for the grid. 0 means disabled. */
/* setWaveId
Call this when the Wave ID has changed (e.g. after a reload). */
- void setWaveId(ID id) { m_waveId = id; };
+ void setWaveId(ID /*id*/ ) { /* TODO m_waveId = id;*/};
private:
std::vector<int> sup; // upper part of the waveform
std::vector<int> inf; // lower part of the waveform
int size; // width of the waveform to draw (in pixel)
- } m_data;
+ } m_waveform;
struct
{
int alloc(int datasize, bool force=false);
- ID m_channelId;
- ID m_waveId;
+ const c::sampleEditor::Data* m_data;
int m_chanStart;
bool m_chanStartLit;
namespace v {
namespace updater
{
-void update(void* p)
+void update(void* /*p*/)
{
if (m::model::waves.changed.load() == true ||
m::model::actions.changed.load() == true ||
- m::model::channels.changed.load() == true)
+ m::model::channels.changed.load() == true)
{
u::gui::rebuild();
m::model::waves.changed.store(false);
* -------------------------------------------------------------------------- */
-#include <atomic>
#include <FL/Fl.H>
#include "core/init.h"
+#include "gui/dialogs/mainWindow.h"
+#ifdef WITH_TESTS
+ #define CATCH_CONFIG_RUNNER
+ #include <vector>
+ #include <string>
+ #include <catch2/catch.hpp>
+ #include "tests/audioBuffer.cpp"
+ #include "tests/rcuList.cpp"
+ #include "tests/recorder.cpp"
+ #include "tests/utils.cpp"
+ #include "tests/wave.cpp"
+ #include "tests/waveFx.cpp"
+ #include "tests/waveManager.cpp"
+#endif
-class gdMainWindow* G_MainWin = nullptr;
+class giada::v::gdMainWindow* G_MainWin = nullptr;
int main(int argc, char** argv)
{
- using namespace giada;
+#ifdef WITH_TESTS
+ std::vector<char*> args(argv, argv + argc);
+ if (args.size() > 1 && strcmp(args[1], "--run-tests") == 0)
+ return Catch::Session().run(args.size() - 1, &args[1]);
+#endif
- m::init::startup(argc, argv);
+ giada::m::init::startup(argc, argv);
int ret = Fl::run();
- m::init::shutdown();
+ giada::m::init::shutdown();
return ret;
-}
-
-
-
+}
\ No newline at end of file
* -------------------------------------------------------------------------- */
+#include <filesystem>
#if defined(_WIN32) // getcwd (unix) or __getcwd (win)
#include <direct.h>
#include <windows.h>
namespace u {
namespace fs
{
-namespace
+bool fileExists(const std::string& s)
{
-std::string normalize_(const std::string& s)
-{
- if (s.back() == G_SLASH) {
- std::string t = s;
- t.pop_back();
- return t;
- }
- return s;
+ return std::filesystem::exists(s);
}
-} // {anonymous}
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
-bool fileExists(const std::string &filename)
+bool isDir(const std::string& s)
{
- FILE* fh = fopen(filename.c_str(), "rb");
- if (!fh) {
- return 0;
- }
- else {
- fclose(fh);
- return 1;
- }
+ return std::filesystem::is_directory(s) && !isProject(s);
}
/* -------------------------------------------------------------------------- */
-bool isDir(const std::string &path)
-{
- bool ret;
-
-#if defined(__linux__) || defined(__FreeBSD__)
-
- struct stat s1;
- stat(path.c_str(), &s1);
- ret = S_ISDIR(s1.st_mode);
-
-#elif defined(__APPLE__)
-
- if (strcmp(path.c_str(), "") == 0)
- ret = false;
- else {
- struct stat s1;
- stat(path.c_str(), &s1);
- ret = S_ISDIR(s1.st_mode);
-
- /* check if ret is a bundle, a special OS X folder which must be
- * shown as a regular file (VST).
- * FIXME - consider native functions CFBundle... */
-
- if (ret) {
- if (fileExists(path + "/Contents/Info.plist"))
- ret = false;
- }
- }
-
-#elif defined(__WIN32)
-
- unsigned dwAttrib = GetFileAttributes(path.c_str());
- ret = (dwAttrib != INVALID_FILE_ATTRIBUTES &&
- (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
-#endif
-
- return ret & !isProject(path);
+bool dirExists(const std::string& s)
+{
+ return std::filesystem::exists(s);
}
/* -------------------------------------------------------------------------- */
-bool dirExists(const std::string &path)
+bool mkdir(const std::string& s)
{
- struct stat st;
- if (stat(path.c_str(), &st) != 0 && errno == ENOENT)
- return false;
- return true;
+ return dirExists(s) ? true : std::filesystem::create_directory(s);
}
/* -------------------------------------------------------------------------- */
-bool mkdir(const std::string &path)
+std::string getRealPath(const std::string& s)
{
-#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
- if (::mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0)
-#else
- if (_mkdir(path.c_str()) == 0)
-#endif
- return true;
- return false;
+ return s.empty() ? "" : std::filesystem::canonical(s).string();
}
std::string basename(const std::string& s)
{
- std::string out = s;
- out.erase(0, out.find_last_of(G_SLASH_STR) + 1);
- return out;
+ return std::filesystem::path(s).filename().string();
}
/* -------------------------------------------------------------------------- */
-std::string dirname(const std::string& path)
+std::string dirname(const std::string& s)
{
- if (path.empty())
- return "";
- std::string out = path;
- out.erase(out.find_last_of(G_SLASH_STR));
- return out;
+ return std::filesystem::path(s).parent_path().string();
}
std::string getCurrentPath()
{
- char buf[PATH_MAX];
-#if defined(__WIN32)
- if (_getcwd(buf, PATH_MAX) != nullptr)
-#else
- if (getcwd(buf, PATH_MAX) != nullptr)
-#endif
- return buf;
- else
- return "";
+ return std::filesystem::current_path().string();
}
/* -------------------------------------------------------------------------- */
-std::string getExt(const std::string& file)
+std::string getExt(const std::string& s)
{
- // TODO - use std functions
- int len = strlen(file.c_str());
- int pos = len;
- while (pos > 0) {
- if (file[pos] == '.')
- break;
- pos--;
- }
- if (pos==0)
- return "";
- std::string out = file;
- return out.substr(pos+1, len);
+ return std::filesystem::path(s).extension().string();
}
std::string stripExt(const std::string& s)
{
- return s.substr(0, s.find_last_of("."));
+ return std::filesystem::path(s).replace_extension("").string();
}
/* -------------------------------------------------------------------------- */
-bool isProject(const std::string& path)
+bool isProject(const std::string& s)
{
- /** FIXME - checks too weak */
-
- if (getExt(path.c_str()) == "gprj" && dirExists(path))
- return 1;
- return 0;
+ /** TODO - checks too weak. */
+ return getExt(s) == ".gprj";
}
/* -------------------------------------------------------------------------- */
-std::string stripFileUrl(const std::string& f)
+std::string stripFileUrl(const std::string& s)
{
- std::string out = f;
+ std::string out = s;
out = u::string::replace(out, "file://", "");
out = u::string::replace(out, "%20", " ");
return out;
std::string getHomePath()
{
- char path[PATH_MAX];
+#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
-#if defined(__linux__) || defined(__FreeBSD__)
+ char buf[PATH_MAX];
+ snprintf(buf, PATH_MAX, "%s/.giada", getenv("HOME"));
- snprintf(path, PATH_MAX, "%s/.giada", getenv("HOME"));
+#elif defined(G_OS_WINDOWS)
-#elif defined(_WIN32)
+ char buf[MAX_PATH];
+ snprintf(buf, MAX_PATH, ".");
- snprintf(path, PATH_MAX, ".");
+#elif defined(G_OS_MAC)
-#elif defined(__APPLE__)
-
- struct passwd* p = getpwuid(getuid());
- if (p == nullptr) {
+ char buf[PATH_MAX];
+ struct passwd* pwd = getpwuid(getuid());
+ if (pwd == nullptr) {
log::print("[getHomePath] unable to fetch user infos\n");
return "";
}
- else {
- const char* home = p->pw_dir;
- snprintf(path, PATH_MAX, "%s/Library/Application Support/Giada", home);
- }
+ const char* home = pwd->pw_dir;
+ snprintf(buf, PATH_MAX, "%s/Library/Application Support/Giada", home);
#endif
- return std::string(path);
+ return std::filesystem::path(buf).string();
}
bool isRootDir(const std::string& s)
{
- if (s == "")
- return false;
-
-#ifdef G_OS_WINDOWS
-
- return s.length() <= 3 && s[1] == ':'; /* X: or X:\ */
-
-#else
-
- return s == G_SLASH_STR;
-
-#endif
+ return std::filesystem::current_path().root_directory() == s;
}
{
#ifdef G_OS_WINDOWS
- /* If root, let the user browse the drives list by returning "". */
+ // If root, let the user browse the drives list by returning "".
if (isRootDir(s))
return "";
#endif
- std::string t = normalize_(s);
- return t.substr(0, t.find_last_of(G_SLASH_STR)) + G_SLASH_STR;
+ return std::filesystem::path(s).parent_path().string();
}
}}} // giada::u::fs::
\ No newline at end of file
namespace giada {
-namespace u {
+namespace u {
namespace fs
{
-bool fileExists(const std::string& path);
-bool dirExists(const std::string& path);
-bool isDir(const std::string& path);
+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& path);
-bool mkdir(const std::string& path);
+bool isProject(const std::string& s);
+bool mkdir(const std::string& s);
std::string getCurrentPath();
std::string getHomePath();
+/* 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 */
#elif defined(__linux__) || defined(__FreeBSD__)
#include <X11/xpm.h>
#endif
-#include "core/channels/channel.h"
#include "core/mixer.h"
#include "core/mixerHandler.h"
#include "core/clock.h"
-#include "core/pluginHost.h"
+#include "core/plugins/pluginHost.h"
#include "core/conf.h"
#include "core/graphics.h"
#include "gui/dialogs/warnings.h"
G_MainWin->mainTimer->setMeter(clock::getBeats(), clock::getBars());
G_MainWin->mainTimer->setBpm(clock::getBpm());
- G_MainWin->mainTimer->setQuantizer(clock::getQuantize());
+ G_MainWin->mainTimer->setQuantizer(clock::getQuantizerValue());
}
/* -------------------------------------------------------------------------- */
+std::string truncate(const std::string& s, Pixel width)
+{
+ if (s.empty() || getStringWidth(s) <= width)
+ return s;
+
+ std::string tmp = s;
+ std::size_t size = tmp.size();
+
+ while (getStringWidth(tmp + "...") > width) {
+ if (size == 0)
+ return "";
+ tmp.resize(--size);
+ }
+
+ return tmp + "...";
+}
+
+/* -------------------------------------------------------------------------- */
+
+
int centerWindowX(int w)
{
return (Fl::w() / 2) - (w / 2);
#include <string>
+#include "core/types.h"
namespace giada
int getStringWidth(const std::string& s);
+/* 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);
#include <cstdio>
-#include <cstdarg>
#include <string>
-#include "utils/fs.h"
-#include "core/const.h"
#include "log.h"
-namespace giada {
-namespace u {
-namespace log
+namespace giada::u::log
{
-namespace
-{
-FILE* f;
-int mode;
-bool stat;
-} // {anonymouse}
-
-
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
-
-
int init(int m)
{
mode = m;
stat = true;
if (mode == LOG_MODE_FILE) {
std::string fpath = fs::getHomePath() + G_SLASH + "giada.log";
- f = fopen(fpath.c_str(), "a");
+ f = std::fopen(fpath.c_str(), "a");
if (!f) {
stat = false;
return 0;
void close()
{
if (mode == LOG_MODE_FILE)
- fclose(f);
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
-void print(const char* format, ...)
-{
- if (mode == LOG_MODE_MUTE)
- return;
- va_list args;
- va_start(args, format);
- if (mode == LOG_MODE_FILE && stat == true) {
- vfprintf(f, format, args);
-#ifdef _WIN32
- fflush(f);
-#endif
- }
- else
- vprintf(format, args);
- va_end(args);
+ std::fclose(f);
}
-}}} // giada::u::log::
\ No newline at end of file
+} // giada::u::log
#define G_UTILS_LOG_H
-namespace giada {
-namespace u {
-namespace log
+#include <cstdio>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include "utils/fs.h"
+#include "core/const.h"
+
+
+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. */
void close();
-void print(const char* format, ...);
-}}} // giada::u::log::
+/* 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::remove_const_t<std::remove_reference_t<
+ decltype(s)>>,
+ 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 <typename... Args>
+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>(args))...);
+#ifdef _WIN32
+ fflush(f);
+#endif
+ }
+ else
+ std::printf(format, string_to_c_str(std::forward<Args>(args))...);
+}
+} // giada::u::log
#endif
#define G_UTILS_MATH_H
+#include <type_traits>
+
+
namespace giada {
namespace u {
namespace math
template <typename TI, typename TO>
TO map(TI x, TI a, TI b, TO w, TO z)
{
+ static_assert(std::is_arithmetic_v<TI>);
+ static_assert(std::is_arithmetic_v<TO>);
+
return (((x - a) / (double) (b - a)) * (z - w)) + w;
}
template <typename TI, typename TO>
TO map(TI x, TI b, TO z)
{
- return (x / (double) b) * z;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-/* bound (1)
-Returns 'def' if 'x' is outside the range ('min', 'max'). */
-
-template <typename T>
-T bound(T x, T min, T max, T def)
-{
- return x < min || x > max ? def : x;
-}
-
-
-/* bound (2)
-Clamps 'x' in the range ('min', 'max'). */
-
-template <typename T>
-T bound(T x, T min, T max)
-{
- if (x < min) return min;
- if (x > max) return max;
- return x;
+ static_assert(std::is_arithmetic_v<TI>);
+ static_assert(std::is_arithmetic_v<TO>);
+
+ return static_cast<TO>((x / (double) b) * z);
}
}}} // giada::u::math::
#include <iomanip>
#include <cstdarg>
#include <climits>
-#include "../core/const.h"
+#include "core/const.h"
#include "string.h"
namespace u {
namespace string
{
-std::string getRealPath(const std::string& path)
-{
- std::string out = "";
-
-#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD) || defined(G_OS_MAC)
-
- char *buf = realpath(path.c_str(), nullptr);
-
-#else // Windows
-
- char *buf = _fullpath(nullptr, path.c_str(), PATH_MAX);
-
-#endif
-
- if (buf) {
- out = buf;
- free(buf);
- }
- return out;
-}
-
-
-/* -------------------------------------------------------------------------- */
-
-
/* TODO - use std::to_string() */
std::string fToString(float f, int precision)
std::string replace(std::string in, const std::string& search, const std::string& replace)
{
- size_t pos = 0;
+ std::size_t pos = 0;
while ((pos = in.find(search, pos)) != std::string::npos) {
in.replace(pos, search.length(), replace);
pos += replace.length();
into account). */
va_start(args, format);
- size_t size = vsnprintf(nullptr, 0, format, args) + 1;
+ 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::vector<std::string> out;
std::string full = in;
std::string token = "";
- size_t curr = 0;
- size_t next = -1;
+ std::size_t curr = 0;
+ std::size_t next = -1;
do {
curr = next + 1;
next = full.find_first_of(sep, curr);
return out.str();
}
-std::string getRealPath(const std::string& path);
-
std::string replace(std::string in, const std::string& search,
const std::string& replace);
{
std::this_thread::sleep_for(std::chrono::milliseconds(millisecs));
}
-}}}; // giada::u::time::
+}}} // giada::u::time::
namespace time
{
void sleep(int millisecs);
-}}};
+}}}
#endif
\ No newline at end of file
namespace vector
{
template <typename T, typename P>
-size_t indexOf(T& v, const P& p)
+std::size_t indexOf(T& v, const P& p)
{
return std::distance(v.begin(), std::find(v.begin(), v.end(), p));
}
-template <typename T, typename P>
-size_t indexOfIf(T& v, P p)
-{
- return std::distance(v.begin(), std::find_if(v.begin(), v.end(), p));
-}
-
-
/* -------------------------------------------------------------------------- */
{
v.erase(std::remove(v.begin(), v.end(), val), v.end());
}
-}}}; // giada::u::vector::
+}}} // giada::u::vector::
#endif
* -------------------------------------------------------------------------- */
+#include <RtMidi.h>
#include "core/const.h"
-#ifdef G_OS_MAC
- #include <RtMidi.h>
-#else
- #include <rtmidi/RtMidi.h>
-#endif
#include <sndfile.h>
#include "deps/rtaudio/RtAudio.h"
#include "ver.h"
return RtMidi::getVersion();
#endif
}
-}}}; // giada::u::ver::
+}}} // giada::u::ver::
std::string getLibsndfileVersion();
std::string getRtAudioVersion();
std::string getRtMidiVersion();
-}}}; // giada::u::ver::
+}}} // giada::u::ver::
#endif
#include <memory>
#include "../src/core/audioBuffer.h"
-#include <catch.hpp>
+#include <catch2/catch.hpp>
TEST_CASE("AudioBuffer")
REQUIRE(buffer.countChannels() == 2);
}
- SECTION("test odd channels count")
- {
- buffer.alloc(BUFFER_SIZE, 7);
- REQUIRE(buffer.countFrames() == BUFFER_SIZE);
- REQUIRE(buffer.countSamples() == BUFFER_SIZE * 7);
- REQUIRE(buffer.countChannels() == 7);
- }
-
buffer.free();
REQUIRE(buffer.countFrames() == 0);
REQUIRE(buffer[1024][0] == 2048.0f);
}
- SECTION("test copy frame")
- {
- buffer.copyFrame(16, &data[32]);
- REQUIRE(buffer[16][0] == 32.0f);
- REQUIRE(buffer[16][1] == 33.0f);
- }
-
delete[] data;
}
}
#define CATCH_CONFIG_MAIN
#define CATCH_CONFIG_FAST_COMPILE
-#include <catch.hpp>
+#include <catch2/catch.hpp>
/* There's no main.cpp in the test suite and the following global var is
unfortunately defined there. Let's fake it. */
#include "../src/core/rcuList.h"
#include "../src/core/types.h"
-#include <catch.hpp>
+#include <catch2/catch.hpp>
using namespace giada;
#include "../src/core/const.h"
#include "../src/core/types.h"
#include "../src/core/action.h"
-#include <catch.hpp>
+#include <catch2/catch.hpp>
TEST_CASE("recorder")
+++ /dev/null
-#include "../src/core/channels/sampleChannel.h"
-#include "../src/core/model/model.h"
-#include <catch.hpp>
-
-
-TEST_CASE("sampleChannel")
-{
- using namespace giada;
- using namespace giada::m;
-
- const int BUFFER_SIZE = 1024;
- const int WAVE_SIZE = 50000;
-
- std::vector<ChannelMode> modes = { ChannelMode::LOOP_BASIC,
- ChannelMode::LOOP_ONCE, ChannelMode::LOOP_REPEAT,
- ChannelMode::LOOP_ONCE_BAR, ChannelMode::SINGLE_BASIC,
- ChannelMode::SINGLE_PRESS, ChannelMode::SINGLE_RETRIG,
- ChannelMode::SINGLE_ENDLESS };
-
- model::channels.clear();
- model::channels.push(std::make_unique<SampleChannel>(false, BUFFER_SIZE, 1, 1));
-
- model::onSwap(model::channels, 1, [&](Channel& c)
- {
- static_cast<SampleChannel&>(c).pushWave(1, WAVE_SIZE);
- });
-
- model::channels.lock();
- SampleChannel& ch = static_cast<SampleChannel&>(*model::channels.get(0));
- model::channels.unlock();
-
- SECTION("push wave")
- {
- REQUIRE(ch.playStatus == ChannelStatus::OFF);
- REQUIRE(ch.begin == 0);
- REQUIRE(ch.end == WAVE_SIZE);
- REQUIRE(ch.name == "");
- }
-
- SECTION("begin/end")
- {
- /* TODO - This section requires model::waves interaction. Let's wait for
- the non-virtual channel refactoring... */
- /*
- ch.setBegin(-100);
-
- REQUIRE(ch.getBegin() == 0);
- REQUIRE(ch.tracker == 0);
- REQUIRE(ch.trackerPreview == 0);
-
- ch.setBegin(100000);
-
- REQUIRE(ch.getBegin() == WAVE_SIZE);
- REQUIRE(ch.tracker == WAVE_SIZE);
- REQUIRE(ch.trackerPreview == WAVE_SIZE);
-
- ch.setBegin(16);
-
- REQUIRE(ch.getBegin() == 16);
- REQUIRE(ch.tracker == 16);
- REQUIRE(ch.trackerPreview == 16);
-
- ch.setEnd(0);
-
- REQUIRE(ch.getEnd() == 17);
-
- ch.setEnd(100000);
-
- REQUIRE(ch.getEnd() == WAVE_SIZE - 1);
-
- ch.setEnd(32);
-
- REQUIRE(ch.getEnd() == 32);
-
- ch.setBegin(64);
-
- REQUIRE(ch.getBegin() == 31);
- */
- }
-
- SECTION("pitch")
- {
- ch.setPitch(40.0f);
-
- REQUIRE(ch.getPitch() == G_MAX_PITCH);
-
- ch.setPitch(-2.0f);
-
- REQUIRE(ch.getPitch() == G_MIN_PITCH);
-
- ch.setPitch(0.8f);
-
- REQUIRE(ch.getPitch() == 0.8f);
- }
-
- SECTION("position")
- {
- REQUIRE(ch.getPosition() == -1); // Initially OFF
-
- ch.playStatus = ChannelStatus::PLAY;
- ch.tracker = 1000;
-
- REQUIRE(ch.getPosition() == 1000);
-
- ch.begin = 700;
-
- REQUIRE(ch.getPosition() == 300);
- }
-
- SECTION("empty")
- {
- ch.empty();
-
- REQUIRE(ch.playStatus == ChannelStatus::EMPTY);
- REQUIRE(ch.begin == 0);
- REQUIRE(ch.end == 0);
- REQUIRE(ch.tracker == 0);
- REQUIRE(ch.volume == G_DEFAULT_VOL);
- REQUIRE(ch.hasActions == false);
- REQUIRE(ch.hasWave == false);
- REQUIRE(ch.waveId == 0);
- }
-
- SECTION("can record audio")
- {
- REQUIRE(ch.canInputRec() == false); // Can't record if not armed
-
- ch.armed = true;
-
- REQUIRE(ch.canInputRec() == false); // Can't record with Wave in it
-
- ch.empty();
-
- REQUIRE(ch.canInputRec() == true);
- }
-
- /* TODO - fillBuffer, isAnyLoopMode, isAnySingleMode, isOnLastFrame */
-}
#include "../src/utils/fs.h"
#include "../src/utils/string.h"
#include "../src/utils/math.h"
-#include <catch.hpp>
+#include <catch2/catch.hpp>
TEST_CASE("u::fs")
{
- using namespace giada::u::fs;
-
- REQUIRE(fileExists("README.md") == true);
- REQUIRE(fileExists("ghost_file") == false);
- REQUIRE(dirExists("src/") == true);
- REQUIRE(dirExists("ghost_dir/") == false);
- REQUIRE(isDir("src/") == true);
- REQUIRE(isDir("giada_tests") == false);
- REQUIRE(basename("tests/utils.cpp") == "utils.cpp");
- REQUIRE(dirname("tests/utils.cpp") == "tests");
- REQUIRE(getExt("tests/utils.cpp") == "cpp");
- REQUIRE(stripExt("tests/utils.cpp") == "tests/utils");
+ 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(isRootDir("C:\\") == true);
- REQUIRE(isRootDir("C:\\path\\to\\something") == false);
- REQUIRE(getUpDir("C:\\path\\to\\something") == "C:\\path\\to\\");
- REQUIRE(getUpDir("C:\\path") == "C:\\");
- REQUIRE(getUpDir("C:\\") == "");
+ REQUIRE(fs::isRootDir("C:\\") == 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:\\") == "");
#else
- REQUIRE(isRootDir("/") == true);
- REQUIRE(isRootDir("/path/to/something") == false);
- REQUIRE(getUpDir("/path/to/something") == "/path/to/");
- REQUIRE(getUpDir("/path") == "/");
- REQUIRE(getUpDir("/") == "/");
+ 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::string;
+ using namespace giada::u;
- REQUIRE(replace("Giada is cool", "cool", "hot") == "Giada is hot");
- REQUIRE(trim(" Giada is cool ") == "Giada is cool");
- REQUIRE(iToString(666) == "666");
- REQUIRE(iToString(0x99AABB, true) == "99AABB");
- REQUIRE(fToString(3.14159, 2) == "3.14");
- REQUIRE(format("I see %d men with %s hats", 5, "strange") == "I see 5 men with strange hats");
+ 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<std::string> v = split("Giada is cool", " ");
+ std::vector<std::string> v = string::split("Giada is cool", " ");
REQUIRE(v.size() == 3);
REQUIRE(v.at(0) == "Giada");
REQUIRE(v.at(1) == "is");
TEST_CASE("::math")
{
- using namespace giada::u::math;
+ using namespace giada::u;
- REQUIRE(map( 0.0f, 0.0f, 30.0f, 0.0f, 1.0f) == 0.0f);
- REQUIRE(map(30.0f, 0.0f, 30.0f, 0.0f, 1.0f) == 1.0f);
- REQUIRE(map(15.0f, 0.0f, 30.0f, 0.0f, 1.0f) == Approx(0.5f));
+ 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));
}
#include <memory>
#include "../src/core/wave.h"
-#include <catch.hpp>
+#include <catch2/catch.hpp>
TEST_CASE("Wave")
#include "../src/core/wave.h"
#include "../src/core/waveFx.h"
#include "../src/core/types.h"
-#include <catch.hpp>
+#include <catch2/catch.hpp>
using namespace giada;
for (int i=a; i<b; i++)
for (int k=0; k<getWave(WAVE_STEREO_ID).getChannels(); k++)
REQUIRE(getWave(WAVE_STEREO_ID)[i][k] == 0.0f);
-
- SECTION("test silence (mono)")
- {
- wfx::silence(getWave(WAVE_MONO_ID).id, a, b);
-
- for (int i=a; i<b; i++)
- for (int k=0; k<getWave(WAVE_MONO_ID).getChannels(); k++)
- REQUIRE(getWave(WAVE_MONO_ID)[i][k] == 0.0f);
- }
}
SECTION("test cut")
wfx::cut(getWave(WAVE_STEREO_ID).id, a, b);
REQUIRE(getWave(WAVE_STEREO_ID).getSize() == prevSize - range);
-
- SECTION("test cut (mono)")
- {
- prevSize = getWave(WAVE_MONO_ID).getSize();
- wfx::cut(getWave(WAVE_MONO_ID).id, a, b);
-
- REQUIRE(getWave(WAVE_MONO_ID).getSize() == prevSize - range);
- }
}
SECTION("test trim")
wfx::trim(getWave(WAVE_STEREO_ID).id, a, b);
REQUIRE(getWave(WAVE_STEREO_ID).getSize() == area);
-
- SECTION("test trim (mono)")
- {
- wfx::trim(getWave(WAVE_MONO_ID).id, a, b);
-
- REQUIRE(getWave(WAVE_MONO_ID).getSize() == area);
- }
}
SECTION("test fade")
int a = 47;
int b = 500;
- wfx::fade(getWave(WAVE_STEREO_ID).id, a, b, wfx::FADE_IN);
- wfx::fade(getWave(WAVE_STEREO_ID).id, a, b, wfx::FADE_OUT);
+ wfx::fade(getWave(WAVE_STEREO_ID).id, a, b, wfx::Fade::IN);
+ wfx::fade(getWave(WAVE_STEREO_ID).id, a, b, wfx::Fade::OUT);
REQUIRE(getWave(WAVE_STEREO_ID).getFrame(a)[0] == 0.0f);
REQUIRE(getWave(WAVE_STEREO_ID).getFrame(a)[1] == 0.0f);
REQUIRE(getWave(WAVE_STEREO_ID).getFrame(b)[0] == 0.0f);
REQUIRE(getWave(WAVE_STEREO_ID).getFrame(b)[1] == 0.0f);
-
- SECTION("test fade (mono)")
- {
- wfx::fade(getWave(WAVE_MONO_ID).id, a, b, wfx::FADE_IN);
- wfx::fade(getWave(WAVE_MONO_ID).id, a, b, wfx::FADE_OUT);
-
- REQUIRE(getWave(WAVE_MONO_ID).getFrame(a)[0] == 0.0f);
- REQUIRE(getWave(WAVE_MONO_ID).getFrame(b)[0] == 0.0f);
- }
}
SECTION("test smooth")
REQUIRE(getWave(WAVE_STEREO_ID).getFrame(a)[1] == 0.0f);
REQUIRE(getWave(WAVE_STEREO_ID).getFrame(b)[0] == 0.0f);
REQUIRE(getWave(WAVE_STEREO_ID).getFrame(b)[1] == 0.0f);
-
- SECTION("test smooth (mono)")
- {
- wfx::smooth(getWave(WAVE_MONO_ID).id, a, b);
- REQUIRE(getWave(WAVE_MONO_ID).getFrame(a)[0] == 0.0f);
- REQUIRE(getWave(WAVE_MONO_ID).getFrame(b)[0] == 0.0f);
- }
}
}
#include <memory>
+#include <samplerate.h>
#include "../src/core/waveManager.h"
#include "../src/core/wave.h"
#include "../src/core/const.h"
-#include <catch.hpp>
+#include <catch2/catch.hpp>
using std::string;
SECTION("test creation")
{
- waveManager::Result res = waveManager::createFromFile("tests/resources/test.wav");
+ 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);
SECTION("test resampling")
{
- waveManager::Result res = waveManager::createFromFile("tests/resources/test.wav");
+ waveManager::Result res = waveManager::createFromFile(TEST_RESOURCES_DIR "test.wav",
+ /*ID=*/0, /*sampleRate=*/G_SAMPLE_RATE, /*quality=*/SRC_LINEAR);
int oldSize = res.wave->getSize();
waveManager::resample(*res.wave.get(), 1, G_SAMPLE_RATE * 2);