New upstream version 0.17.1+ds1
authorIOhannes m zmölnig <zmoelnig@umlautS.umlaeute.mur.at>
Tue, 2 Feb 2021 21:14:14 +0000 (22:14 +0100)
committerIOhannes m zmölnig <zmoelnig@umlautS.umlaeute.mur.at>
Tue, 2 Feb 2021 21:14:14 +0000 (22:14 +0100)
336 files changed:
CMakeLists.txt [new file with mode: 0644]
ChangeLog
Makefile.am
README.md
configure.ac
extras/giada-logo.svg [new file with mode: 0644]
extras/giada.icns [new file with mode: 0644]
scripts/create_source_tarball.sh [new file with mode: 0755]
src/core/.dirstamp [deleted file]
src/core/audioBuffer.cpp
src/core/audioBuffer.h
src/core/channels/audioReceiver.cpp [new file with mode: 0644]
src/core/channels/audioReceiver.h [new file with mode: 0644]
src/core/channels/channel.cpp
src/core/channels/channel.h
src/core/channels/channelManager.cpp
src/core/channels/channelManager.h
src/core/channels/masterChannel.cpp [deleted file]
src/core/channels/masterChannel.h [deleted file]
src/core/channels/midiActionRecorder.cpp [new file with mode: 0644]
src/core/channels/midiActionRecorder.h [new file with mode: 0644]
src/core/channels/midiChannel.cpp [deleted file]
src/core/channels/midiChannel.h [deleted file]
src/core/channels/midiChannelProc.cpp [deleted file]
src/core/channels/midiChannelProc.h [deleted file]
src/core/channels/midiController.cpp [new file with mode: 0644]
src/core/channels/midiController.h [new file with mode: 0644]
src/core/channels/midiLearner.cpp [new file with mode: 0644]
src/core/channels/midiLearner.h [new file with mode: 0644]
src/core/channels/midiLighter.cpp [new file with mode: 0644]
src/core/channels/midiLighter.h [new file with mode: 0644]
src/core/channels/midiReceiver.cpp [new file with mode: 0644]
src/core/channels/midiReceiver.h [new file with mode: 0644]
src/core/channels/midiSender.cpp [new file with mode: 0644]
src/core/channels/midiSender.h [new file with mode: 0644]
src/core/channels/sampleActionRecorder.cpp [new file with mode: 0644]
src/core/channels/sampleActionRecorder.h [new file with mode: 0644]
src/core/channels/sampleChannel.cpp [deleted file]
src/core/channels/sampleChannel.h [deleted file]
src/core/channels/sampleChannelProc.cpp [deleted file]
src/core/channels/sampleChannelProc.h [deleted file]
src/core/channels/sampleChannelRec.cpp [deleted file]
src/core/channels/sampleChannelRec.h [deleted file]
src/core/channels/sampleController.cpp [new file with mode: 0644]
src/core/channels/sampleController.h [new file with mode: 0644]
src/core/channels/samplePlayer.cpp [new file with mode: 0644]
src/core/channels/samplePlayer.h [new file with mode: 0644]
src/core/channels/state.cpp [new file with mode: 0644]
src/core/channels/state.h [new file with mode: 0644]
src/core/channels/waveReader.cpp [new file with mode: 0644]
src/core/channels/waveReader.h [new file with mode: 0644]
src/core/clock.cpp
src/core/clock.h
src/core/conf.cpp
src/core/conf.h
src/core/const.h
src/core/graphics.cpp
src/core/graphics.h
src/core/idManager.cpp
src/core/idManager.h
src/core/init.cpp
src/core/kernelAudio.cpp
src/core/kernelAudio.h
src/core/kernelMidi.cpp
src/core/kernelMidi.h
src/core/midiDispatcher.cpp
src/core/midiDispatcher.h
src/core/midiEvent.cpp
src/core/midiEvent.h
src/core/midiLearnParam.cpp [new file with mode: 0644]
src/core/midiLearnParam.h [new file with mode: 0644]
src/core/midiMapConf.cpp
src/core/midiMapConf.h
src/core/mixer.cpp
src/core/mixer.h
src/core/mixerHandler.cpp
src/core/mixerHandler.h
src/core/model/model.cpp
src/core/model/model.h
src/core/model/storage.cpp
src/core/model/traits.h [new file with mode: 0644]
src/core/patch.cpp
src/core/patch.h
src/core/plugin.cpp [deleted file]
src/core/plugin.h [deleted file]
src/core/pluginHost.cpp [deleted file]
src/core/pluginHost.h [deleted file]
src/core/pluginManager.cpp [deleted file]
src/core/pluginManager.h [deleted file]
src/core/plugins/plugin.cpp [new file with mode: 0644]
src/core/plugins/plugin.h [new file with mode: 0644]
src/core/plugins/pluginHost.cpp [new file with mode: 0644]
src/core/plugins/pluginHost.h [new file with mode: 0644]
src/core/plugins/pluginManager.cpp [new file with mode: 0644]
src/core/plugins/pluginManager.h [new file with mode: 0644]
src/core/plugins/pluginState.cpp [new file with mode: 0644]
src/core/plugins/pluginState.h [new file with mode: 0644]
src/core/quantizer.cpp [new file with mode: 0644]
src/core/quantizer.h [new file with mode: 0644]
src/core/queue.h
src/core/range.h
src/core/rcuList.h
src/core/recManager.cpp
src/core/recorder.cpp
src/core/recorder.h
src/core/recorderHandler.cpp
src/core/recorderHandler.h
src/core/ringBuffer.h [new file with mode: 0644]
src/core/sequencer.cpp [new file with mode: 0644]
src/core/sequencer.h [new file with mode: 0644]
src/core/types.h
src/core/wave.cpp
src/core/wave.h
src/core/waveFx.cpp
src/core/waveFx.h
src/core/waveManager.cpp
src/core/waveManager.h
src/deps/juce-config.h
src/deps/rtaudio/RtAudio.cpp [deleted file]
src/deps/rtaudio/RtAudio.h [deleted file]
src/deps/rtaudio/include/asio.cpp [deleted file]
src/deps/rtaudio/include/asio.h [deleted file]
src/deps/rtaudio/include/asiodrivers.cpp [deleted file]
src/deps/rtaudio/include/asiodrivers.h [deleted file]
src/deps/rtaudio/include/asiodrvr.h [deleted file]
src/deps/rtaudio/include/asiolist.cpp [deleted file]
src/deps/rtaudio/include/asiolist.h [deleted file]
src/deps/rtaudio/include/asiosys.h [deleted file]
src/deps/rtaudio/include/dsound.h [deleted file]
src/deps/rtaudio/include/functiondiscoverykeys_devpkey.h [deleted file]
src/deps/rtaudio/include/ginclude.h [deleted file]
src/deps/rtaudio/include/iasiodrv.h [deleted file]
src/deps/rtaudio/include/iasiothiscallresolver.cpp [deleted file]
src/deps/rtaudio/include/iasiothiscallresolver.h [deleted file]
src/deps/rtaudio/include/soundcard.h [deleted file]
src/ext/resource.h
src/glue/actionEditor.cpp
src/glue/actionEditor.h
src/glue/channel.cpp
src/glue/channel.h
src/glue/events.cpp [new file with mode: 0644]
src/glue/events.h [new file with mode: 0644]
src/glue/io.cpp
src/glue/io.h
src/glue/main.cpp
src/glue/main.h
src/glue/plugin.cpp
src/glue/plugin.h
src/glue/recorder.cpp
src/glue/sampleEditor.cpp
src/glue/sampleEditor.h
src/glue/storage.cpp
src/glue/transport.cpp [deleted file]
src/gui/dialogs/about.cpp
src/gui/dialogs/about.h
src/gui/dialogs/actionEditor/baseActionEditor.cpp
src/gui/dialogs/actionEditor/baseActionEditor.h
src/gui/dialogs/actionEditor/midiActionEditor.cpp
src/gui/dialogs/actionEditor/sampleActionEditor.cpp
src/gui/dialogs/actionEditor/sampleActionEditor.h
src/gui/dialogs/beatsInput.cpp
src/gui/dialogs/beatsInput.h
src/gui/dialogs/bpmInput.cpp
src/gui/dialogs/bpmInput.h
src/gui/dialogs/browser/browserBase.cpp
src/gui/dialogs/browser/browserBase.h
src/gui/dialogs/browser/browserDir.cpp
src/gui/dialogs/browser/browserDir.h
src/gui/dialogs/browser/browserLoad.cpp
src/gui/dialogs/browser/browserLoad.h
src/gui/dialogs/browser/browserSave.cpp
src/gui/dialogs/browser/browserSave.h
src/gui/dialogs/channelNameInput.cpp
src/gui/dialogs/channelNameInput.h
src/gui/dialogs/config.cpp
src/gui/dialogs/config.h
src/gui/dialogs/keyGrabber.cpp
src/gui/dialogs/keyGrabber.h
src/gui/dialogs/mainWindow.cpp
src/gui/dialogs/midiIO/midiInputBase.cpp
src/gui/dialogs/midiIO/midiInputBase.h
src/gui/dialogs/midiIO/midiInputChannel.cpp
src/gui/dialogs/midiIO/midiInputChannel.h
src/gui/dialogs/midiIO/midiInputMaster.cpp
src/gui/dialogs/midiIO/midiInputMaster.h
src/gui/dialogs/midiIO/midiOutputBase.cpp
src/gui/dialogs/midiIO/midiOutputBase.h
src/gui/dialogs/midiIO/midiOutputMidiCh.cpp
src/gui/dialogs/midiIO/midiOutputMidiCh.h
src/gui/dialogs/midiIO/midiOutputSampleCh.cpp
src/gui/dialogs/midiIO/midiOutputSampleCh.h
src/gui/dialogs/pluginChooser.cpp
src/gui/dialogs/pluginChooser.h
src/gui/dialogs/pluginList.cpp
src/gui/dialogs/pluginList.h
src/gui/dialogs/pluginWindow.cpp
src/gui/dialogs/pluginWindow.h
src/gui/dialogs/pluginWindowGUI.cpp
src/gui/dialogs/pluginWindowGUI.h
src/gui/dialogs/sampleEditor.cpp
src/gui/dialogs/sampleEditor.h
src/gui/dialogs/window.cpp
src/gui/dialogs/window.h
src/gui/dispatcher.cpp
src/gui/dispatcher.h
src/gui/elems/actionEditor/baseAction.cpp
src/gui/elems/actionEditor/baseActionEditor.cpp
src/gui/elems/actionEditor/baseActionEditor.h
src/gui/elems/actionEditor/envelopeEditor.cpp
src/gui/elems/actionEditor/envelopeEditor.h
src/gui/elems/actionEditor/gridTool.cpp
src/gui/elems/actionEditor/gridTool.h
src/gui/elems/actionEditor/noteEditor.cpp
src/gui/elems/actionEditor/noteEditor.h
src/gui/elems/actionEditor/pianoRoll.cpp
src/gui/elems/actionEditor/pianoRoll.h
src/gui/elems/actionEditor/sampleAction.cpp
src/gui/elems/actionEditor/sampleActionEditor.cpp
src/gui/elems/actionEditor/sampleActionEditor.h
src/gui/elems/actionEditor/velocityEditor.cpp
src/gui/elems/actionEditor/velocityEditor.h
src/gui/elems/basics/baseButton.cpp [deleted file]
src/gui/elems/basics/baseButton.h [deleted file]
src/gui/elems/basics/box.cpp
src/gui/elems/basics/box.h
src/gui/elems/basics/boxtypes.cpp
src/gui/elems/basics/boxtypes.h
src/gui/elems/basics/button.cpp
src/gui/elems/basics/button.h
src/gui/elems/basics/check.cpp
src/gui/elems/basics/check.h
src/gui/elems/basics/choice.cpp
src/gui/elems/basics/choice.h
src/gui/elems/basics/group.cpp [new file with mode: 0644]
src/gui/elems/basics/group.h [new file with mode: 0644]
src/gui/elems/basics/pack.cpp [new file with mode: 0644]
src/gui/elems/basics/pack.h [new file with mode: 0644]
src/gui/elems/basics/scroll.cpp
src/gui/elems/basics/scrollPack.cpp [new file with mode: 0644]
src/gui/elems/basics/scrollPack.h [new file with mode: 0644]
src/gui/elems/browser.cpp
src/gui/elems/config/tabAudio.cpp
src/gui/elems/config/tabAudio.h
src/gui/elems/config/tabBehaviors.cpp
src/gui/elems/config/tabBehaviors.h
src/gui/elems/config/tabMidi.cpp
src/gui/elems/config/tabMidi.h
src/gui/elems/config/tabMisc.h
src/gui/elems/config/tabPlugins.cpp
src/gui/elems/config/tabPlugins.h
src/gui/elems/mainWindow/keyboard/channel.cpp
src/gui/elems/mainWindow/keyboard/channel.h
src/gui/elems/mainWindow/keyboard/channelButton.cpp
src/gui/elems/mainWindow/keyboard/channelButton.h
src/gui/elems/mainWindow/keyboard/channelMode.cpp
src/gui/elems/mainWindow/keyboard/channelMode.h
src/gui/elems/mainWindow/keyboard/channelStatus.cpp
src/gui/elems/mainWindow/keyboard/channelStatus.h
src/gui/elems/mainWindow/keyboard/column.cpp
src/gui/elems/mainWindow/keyboard/column.h
src/gui/elems/mainWindow/keyboard/keyboard.cpp
src/gui/elems/mainWindow/keyboard/keyboard.h
src/gui/elems/mainWindow/keyboard/midiChannel.cpp
src/gui/elems/mainWindow/keyboard/midiChannel.h
src/gui/elems/mainWindow/keyboard/midiChannelButton.cpp
src/gui/elems/mainWindow/keyboard/midiChannelButton.h
src/gui/elems/mainWindow/keyboard/sampleChannel.cpp
src/gui/elems/mainWindow/keyboard/sampleChannel.h
src/gui/elems/mainWindow/keyboard/sampleChannelButton.cpp
src/gui/elems/mainWindow/keyboard/sampleChannelButton.h
src/gui/elems/mainWindow/mainIO.cpp
src/gui/elems/mainWindow/mainIO.h
src/gui/elems/mainWindow/mainMenu.cpp
src/gui/elems/mainWindow/mainMenu.h
src/gui/elems/mainWindow/mainTimer.cpp
src/gui/elems/mainWindow/mainTimer.h
src/gui/elems/mainWindow/mainTransport.cpp
src/gui/elems/mainWindow/mainTransport.h
src/gui/elems/midiIO/midiLearner.cpp [new file with mode: 0644]
src/gui/elems/midiIO/midiLearner.h [new file with mode: 0644]
src/gui/elems/midiIO/midiLearnerBase.cpp [deleted file]
src/gui/elems/midiIO/midiLearnerBase.h [deleted file]
src/gui/elems/midiIO/midiLearnerChannel.cpp [deleted file]
src/gui/elems/midiIO/midiLearnerChannel.h [deleted file]
src/gui/elems/midiIO/midiLearnerMaster.cpp [deleted file]
src/gui/elems/midiIO/midiLearnerMaster.h [deleted file]
src/gui/elems/midiIO/midiLearnerPack.cpp [new file with mode: 0644]
src/gui/elems/midiIO/midiLearnerPack.h [new file with mode: 0644]
src/gui/elems/midiIO/midiLearnerPlugin.cpp [deleted file]
src/gui/elems/midiIO/midiLearnerPlugin.h [deleted file]
src/gui/elems/plugin/pluginBrowser.cpp
src/gui/elems/plugin/pluginElement.cpp
src/gui/elems/plugin/pluginElement.h
src/gui/elems/plugin/pluginParameter.cpp
src/gui/elems/plugin/pluginParameter.h
src/gui/elems/sampleEditor/boostTool.cpp
src/gui/elems/sampleEditor/boostTool.h
src/gui/elems/sampleEditor/panTool.cpp
src/gui/elems/sampleEditor/panTool.h
src/gui/elems/sampleEditor/pitchTool.cpp
src/gui/elems/sampleEditor/pitchTool.h
src/gui/elems/sampleEditor/rangeTool.cpp
src/gui/elems/sampleEditor/rangeTool.h
src/gui/elems/sampleEditor/shiftTool.cpp
src/gui/elems/sampleEditor/shiftTool.h
src/gui/elems/sampleEditor/volumeTool.cpp
src/gui/elems/sampleEditor/volumeTool.h
src/gui/elems/sampleEditor/waveTools.cpp
src/gui/elems/sampleEditor/waveTools.h
src/gui/elems/sampleEditor/waveform.cpp
src/gui/elems/sampleEditor/waveform.h
src/gui/updater.cpp
src/main.cpp
src/utils/fs.cpp
src/utils/fs.h
src/utils/gui.cpp
src/utils/gui.h
src/utils/log.cpp
src/utils/log.h
src/utils/math.h
src/utils/string.cpp
src/utils/string.h
src/utils/time.cpp
src/utils/time.h
src/utils/vector.h
src/utils/ver.cpp
src/utils/ver.h
tests/audioBuffer.cpp
tests/main.cpp
tests/rcuList.cpp
tests/recorder.cpp
tests/sampleChannel.cpp [deleted file]
tests/utils.cpp
tests/wave.cpp
tests/waveFx.cpp
tests/waveManager.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0a5118b
--- /dev/null
@@ -0,0 +1,515 @@
+# ------------------------------------------------------------------------------
+# 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()
index 655967fa37906733052b4d4fc8c8cbc618262b1d..6cb225bb1b885c065502f1a91f453fffe24499e5 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
 --------------------------------------------------------------------------------
 
 
+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
index 9bd86b9e02bae36d3023c7231b6aa5459cc6d19f..ded463ab96572d1c4a3b55f5a1411c83a5606660 100644 (file)
@@ -6,7 +6,7 @@ AUTOMAKE_OPTIONS = foreign
 
 
 cppFlags = -I$(top_srcdir)/src
-cxxFlags = -std=c++14 -Wall
+cxxFlags = -std=c++17 -Wall
 ldAdd =
 ldFlags =
 sourcesExtra = 
@@ -14,6 +14,7 @@ sourcesMain = src/main.cpp
 sourcesCore =                               \
        src/core/const.h                        \
        src/core/queue.h                        \
+       src/core/ringBuffer.h                   \
        src/core/types.h                        \
        src/core/range.h                        \
        src/core/action.h                       \
@@ -25,20 +26,26 @@ sourcesCore =                               \
        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                       \
@@ -47,6 +54,7 @@ sourcesCore =                               \
        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              \
@@ -61,28 +69,42 @@ sourcesCore =                               \
        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                           \
@@ -161,6 +183,10 @@ sourcesCore =                               \
        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                               \
@@ -199,7 +225,7 @@ sourcesCore =                               \
        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            \
@@ -249,20 +275,16 @@ sourcesCore =                               \
        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          \
@@ -303,7 +325,7 @@ sourcesCore =                               \
        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               \
@@ -313,9 +335,7 @@ sourcesTests =                   \
        tests/utils.cpp              \
        tests/recorder.cpp           \
        tests/waveFx.cpp             \
-       tests/audioBuffer.cpp        \
-       tests/sampleChannel.cpp
-
+       tests/audioBuffer.cpp
 if WITH_VST
 
 sourcesExtra += \
@@ -411,7 +431,7 @@ cppFlags += -D__MACOSX_CORE__
 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 \
@@ -423,7 +443,7 @@ endif
 
 bin_PROGRAMS = giada
 
-giada_SOURCES = $(sourcesCore) $(sourcesMain) $(sourcesExtra)
+giada_SOURCES = $(sourcesCore) $(sourcesExtra) $(sourcesMain)
 giada_CPPFLAGS = $(cppFlags)
 giada_CXXFLAGS = $(cxxFlags)
 giada_LDADD = $(ldAdd)
index 10a1809ef82f51abd46c339db33bdba6ed17dae5..4d1978c6d8db61a99be8014e8efe21ae0f2a1846 100644 (file)
--- a/README.md
+++ b/README.md
@@ -3,36 +3,40 @@
 </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>
 
-![Giada Loop Machine screenshot](http://giadamusic.com/public/img/screenshots/giada-loop-machine-screenshot-14-carousel.jpg)
+![Giada Loop Machine screenshot](https://giadamusic.com/images/giada-canvas.png)
 
 ## Main features
 
+* Your sample player! Load samples from your crates and play them with a computer keyboard or a MIDI controller;
+* Your loop machine! Build your performance in real time by layering audio tracks or MIDI events, driven by the main sequencer;
+* Your song editor! Write songs from scratch or edit existing live recordings with the powerful Action Editor, for a fine-tuned control;
+* Your live recorder! Record sounds from the real world and MIDI events coming from external devices or other apps;
+* Your FX processor! Process samples or audio/MIDI input signals with VST instruments from your plug-ins collection;
+* Your MIDI controller! Control other software or synchronize physical MIDI devices by using Giada as a MIDI master sequencer.
+
+### And more:
+
 * Ultra-lightweight internal design;
 * multi-thread/multi-core support;
 * 32-bit floating point audio engine;
 * ALSA, JACK + Transport, CoreAudio, ASIO and DirectSound full support;
-* 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;
index d7121b32033cbc77a1bd6e4edb1ee98b59d02d54..f9c7337a5459ea4bcf36937df3aede37a34b86bf 100644 (file)
@@ -58,7 +58,7 @@ AM_CONDITIONAL(FREEBSD, test "x$os" = "xfreebsd")
 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)]
 )
 
@@ -70,7 +70,7 @@ AC_ARG_ENABLE(
 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)]
 )
 
@@ -81,7 +81,7 @@ AC_ARG_ENABLE(
 AC_ARG_ENABLE(
        [debug],
        AS_HELP_STRING([--enable-debug], [enable debug mode (asserts, ...)]),
-  [],
+       [],
        [AC_DEFINE(NDEBUG)]
 )
 
diff --git a/extras/giada-logo.svg b/extras/giada-logo.svg
new file mode 100644 (file)
index 0000000..17e6885
--- /dev/null
@@ -0,0 +1,17 @@
+<?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>
diff --git a/extras/giada.icns b/extras/giada.icns
new file mode 100644 (file)
index 0000000..627e605
Binary files /dev/null and b/extras/giada.icns differ
diff --git a/scripts/create_source_tarball.sh b/scripts/create_source_tarball.sh
new file mode 100755 (executable)
index 0000000..aff5570
--- /dev/null
@@ -0,0 +1,130 @@
+#!/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:
diff --git a/src/core/.dirstamp b/src/core/.dirstamp
deleted file mode 100644 (file)
index e69de29..0000000
index ddb9024a76f4ae172d24bb0aa1438a037ddcd450..483e3c45d9e4152c62b9fa8e3ab034bd1d3af8a6 100644 (file)
@@ -1,6 +1,32 @@
-#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"
 
 
@@ -8,10 +34,29 @@ namespace giada {
 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); 
 }
 
 
@@ -27,7 +72,7 @@ AudioBuffer::~AudioBuffer()
 /* -------------------------------------------------------------------------- */
 
 
-float* AudioBuffer::operator [](int offset) const
+float* AudioBuffer::operator [](Frame offset) const
 {
        assert(m_data != nullptr);
        assert(offset < m_size);
@@ -38,35 +83,49 @@ float* AudioBuffer::operator [](int offset) const
 /* -------------------------------------------------------------------------- */
 
 
-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();
 }
 
 
@@ -75,7 +134,7 @@ void AudioBuffer::alloc(int size, int channels)
 
 void AudioBuffer::free()
 {
-       delete[] m_data;  // No check required, delete nullptr does nothing
+       delete[] m_data;
        setData(nullptr, 0, 0);
 }
 
@@ -83,8 +142,10 @@ void AudioBuffer::free()
 /* -------------------------------------------------------------------------- */
 
 
-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;
@@ -96,6 +157,8 @@ void AudioBuffer::setData(float* data, int size, int channels)
 
 void AudioBuffer::moveData(AudioBuffer& b)
 {
+       assert(b.countChannels() <= NUM_CHANS);
+
        free();
        m_data     = b[0];
        m_size     = b.countFrames();
@@ -107,20 +170,52 @@ void AudioBuffer::moveData(AudioBuffer& b)
 /* -------------------------------------------------------------------------- */
 
 
-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
index ab6fcd32971b30fbe8cbcb51c2f5d8a15f24afaa..67247aa56d7c19be453b803dea0d3ac28cf84ca3 100644 (file)
@@ -1,16 +1,65 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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 []
@@ -26,35 +75,49 @@ public:
 
        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);
 
@@ -62,12 +125,14 @@ public:
        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;
 };
 
diff --git a/src/core/channels/audioReceiver.cpp b/src/core/channels/audioReceiver.cpp
new file mode 100644 (file)
index 0000000..dd222e6
--- /dev/null
@@ -0,0 +1,78 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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::
diff --git a/src/core/channels/audioReceiver.h b/src/core/channels/audioReceiver.h
new file mode 100644 (file)
index 0000000..443bd3c
--- /dev/null
@@ -0,0 +1,75 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
index 2c98f8bee0c5d80c11cdc3b80128d1ffa6a977fe..8c7845bbf2f16dc59f9d13c0d2a6365f0134ce03 100644 (file)
 
 
 #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;
+       }
 }
 
 
@@ -95,152 +72,160 @@ Channel::Channel(ChannelType type, ChannelStatus playStatus, int bufferSize,
 
 
 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;
        }
 }
 
@@ -248,82 +233,118 @@ void Channel::sendMidiLstatus()
 /* -------------------------------------------------------------------------- */
 
 
-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::
index a6d4e5f3c99577d50c05c01933f440c3c7bb080b..5e7b9127629dffbc1a1eef68091c9723f666597f 100644 (file)
 #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::
 
 
index 8bc2b43f6895ae5c13cabcea88d4bb750306afa0..bbf3bceec1341fc70cbf870eaa541b05969e3f40 100644 (file)
 
 #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"
@@ -69,24 +68,11 @@ void init()
 /* -------------------------------------------------------------------------- */
 
 
-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;
 }
 
@@ -96,22 +82,10 @@ std::unique_ptr<Channel> create(ChannelType type, int bufferSize,
 
 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;
 }
 
@@ -121,19 +95,8 @@ std::unique_ptr<Channel> create(const Channel& o)
 
 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);
 }
 
 
@@ -144,60 +107,58 @@ const patch::Channel serializeChannel(const Channel& c)
 {
        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
index 4229a047da54aae8885ab613feec453a2ccf0f69..d7eb6f4e02e0bc6561b7db58b4d835155f0aa732 100644 (file)
 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
@@ -53,8 +56,7 @@ void 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). */
@@ -66,7 +68,7 @@ Creates a new Channel given the patch raw data and vice versa. */
 
 std::unique_ptr<Channel> deserializeChannel(const patch::Channel& c, int bufferSize);
 const patch::Channel     serializeChannel(const Channel& c);
-}}}; // giada::m::channelManager
+}}} // giada::m::channelManager
 
 
 #endif
diff --git a/src/core/channels/masterChannel.cpp b/src/core/channels/masterChannel.cpp
deleted file mode 100644 (file)
index 9ce1735..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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::
diff --git a/src/core/channels/masterChannel.h b/src/core/channels/masterChannel.h
deleted file mode 100644 (file)
index 1e336c6..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
diff --git a/src/core/channels/midiActionRecorder.cpp b/src/core/channels/midiActionRecorder.cpp
new file mode 100644 (file)
index 0000000..7d27433
--- /dev/null
@@ -0,0 +1,91 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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::
+
diff --git a/src/core/channels/midiActionRecorder.h b/src/core/channels/midiActionRecorder.h
new file mode 100644 (file)
index 0000000..50e3109
--- /dev/null
@@ -0,0 +1,62 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/channels/midiChannel.cpp b/src/core/channels/midiChannel.cpp
deleted file mode 100644 (file)
index 89dae2d..0000000
+++ /dev/null
@@ -1,226 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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::
diff --git a/src/core/channels/midiChannel.h b/src/core/channels/midiChannel.h
deleted file mode 100644 (file)
index 7f2f550..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
diff --git a/src/core/channels/midiChannelProc.cpp b/src/core/channels/midiChannelProc.cpp
deleted file mode 100644 (file)
index 204f5a6..0000000
+++ /dev/null
@@ -1,215 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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);
-}
-}}};
diff --git a/src/core/channels/midiChannelProc.h b/src/core/channels/midiChannelProc.h
deleted file mode 100644 (file)
index 724759f..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
diff --git a/src/core/channels/midiController.cpp b/src/core/channels/midiController.cpp
new file mode 100644 (file)
index 0000000..55dcd03
--- /dev/null
@@ -0,0 +1,126 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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::
diff --git a/src/core/channels/midiController.h b/src/core/channels/midiController.h
new file mode 100644 (file)
index 0000000..7a43778
--- /dev/null
@@ -0,0 +1,62 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/channels/midiLearner.cpp b/src/core/channels/midiLearner.cpp
new file mode 100644 (file)
index 0000000..0386546
--- /dev/null
@@ -0,0 +1,57 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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::
diff --git a/src/core/channels/midiLearner.h b/src/core/channels/midiLearner.h
new file mode 100644 (file)
index 0000000..5acb93d
--- /dev/null
@@ -0,0 +1,55 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/channels/midiLighter.cpp b/src/core/channels/midiLighter.cpp
new file mode 100644 (file)
index 0000000..a82a9c0
--- /dev/null
@@ -0,0 +1,149 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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::
diff --git a/src/core/channels/midiLighter.h b/src/core/channels/midiLighter.h
new file mode 100644 (file)
index 0000000..8b77b29
--- /dev/null
@@ -0,0 +1,73 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/channels/midiReceiver.cpp b/src/core/channels/midiReceiver.cpp
new file mode 100644 (file)
index 0000000..2afa74a
--- /dev/null
@@ -0,0 +1,131 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/channels/midiReceiver.h b/src/core/channels/midiReceiver.h
new file mode 100644 (file)
index 0000000..dc83bbe
--- /dev/null
@@ -0,0 +1,89 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/channels/midiSender.cpp b/src/core/channels/midiSender.cpp
new file mode 100644 (file)
index 0000000..a5a616a
--- /dev/null
@@ -0,0 +1,92 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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::
diff --git a/src/core/channels/midiSender.h b/src/core/channels/midiSender.h
new file mode 100644 (file)
index 0000000..5f2d51f
--- /dev/null
@@ -0,0 +1,65 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/channels/sampleActionRecorder.cpp b/src/core/channels/sampleActionRecorder.cpp
new file mode 100644 (file)
index 0000000..a546556
--- /dev/null
@@ -0,0 +1,253 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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::
+
diff --git a/src/core/channels/sampleActionRecorder.h b/src/core/channels/sampleActionRecorder.h
new file mode 100644 (file)
index 0000000..b6bf451
--- /dev/null
@@ -0,0 +1,77 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/channels/sampleChannel.cpp b/src/core/channels/sampleChannel.cpp
deleted file mode 100644 (file)
index 73eb4f0..0000000
+++ /dev/null
@@ -1,553 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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::
diff --git a/src/core/channels/sampleChannel.h b/src/core/channels/sampleChannel.h
deleted file mode 100644 (file)
index 21f8388..0000000
+++ /dev/null
@@ -1,175 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
diff --git a/src/core/channels/sampleChannelProc.cpp b/src/core/channels/sampleChannelProc.cpp
deleted file mode 100644 (file)
index cd8b193..0000000
+++ /dev/null
@@ -1,526 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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);
-}
-}}};
diff --git a/src/core/channels/sampleChannelProc.h b/src/core/channels/sampleChannelProc.h
deleted file mode 100644 (file)
index 075f125..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
diff --git a/src/core/channels/sampleChannelRec.cpp b/src/core/channels/sampleChannelRec.cpp
deleted file mode 100644 (file)
index 1b74a61..0000000
+++ /dev/null
@@ -1,276 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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);
-}
-}}};
diff --git a/src/core/channels/sampleChannelRec.h b/src/core/channels/sampleChannelRec.h
deleted file mode 100644 (file)
index 29a9240..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
diff --git a/src/core/channels/sampleController.cpp b/src/core/channels/sampleController.cpp
new file mode 100644 (file)
index 0000000..d6055b2
--- /dev/null
@@ -0,0 +1,414 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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::
diff --git a/src/core/channels/sampleController.h b/src/core/channels/sampleController.h
new file mode 100644 (file)
index 0000000..7a38dd2
--- /dev/null
@@ -0,0 +1,76 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/channels/samplePlayer.cpp b/src/core/channels/samplePlayer.cpp
new file mode 100644 (file)
index 0000000..1fc5723
--- /dev/null
@@ -0,0 +1,257 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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::
diff --git a/src/core/channels/samplePlayer.h b/src/core/channels/samplePlayer.h
new file mode 100644 (file)
index 0000000..2ddfbc5
--- /dev/null
@@ -0,0 +1,117 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/channels/state.cpp b/src/core/channels/state.cpp
new file mode 100644 (file)
index 0000000..b3dbd0e
--- /dev/null
@@ -0,0 +1,318 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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::
diff --git a/src/core/channels/state.h b/src/core/channels/state.h
new file mode 100644 (file)
index 0000000..9e69e2c
--- /dev/null
@@ -0,0 +1,237 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/channels/waveReader.cpp b/src/core/channels/waveReader.cpp
new file mode 100644 (file)
index 0000000..9f57174
--- /dev/null
@@ -0,0 +1,176 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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::
diff --git a/src/core/channels/waveReader.h b/src/core/channels/waveReader.h
new file mode 100644 (file)
index 0000000..1d6bc44
--- /dev/null
@@ -0,0 +1,74 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
index 5a84d3add19abefc0b70ff939a3ff706a5b82732..2dd28ef105ab35fc1dd4bc391d7a98233b1434e6 100644 (file)
 #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
 {
@@ -48,7 +48,13 @@ std::atomic<int> currentFrameWait_(0);
 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;
@@ -56,8 +62,7 @@ int midiTCseconds_ = 0;
 int midiTCminutes_ = 0;
 int midiTChours_   = 0;
 
-
-#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD)
+#ifdef WITH_AUDIO_JACK
 kernelAudio::JackState jackStatePrev_;
 #endif
 
@@ -69,15 +74,15 @@ Updates bpm, frames, beats and so on. Private version. */
 
 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}
 
 
 /* -------------------------------------------------------------------------- */
@@ -87,7 +92,7 @@ void recomputeFrames_(model::Clock& c)
 
 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)
        {
@@ -131,7 +136,7 @@ bool isActive()
 
 bool quantoHasPassed()
 {
-       return currentFrame_.load() % quanto_ == 0;
+       return clock::getQuantizerValue() != 0 && currentFrame_.load() % quantizerStep_ == 0;
 }
 
 
@@ -172,16 +177,7 @@ bool isOnFirstBeat()
 
 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)
        {
@@ -193,8 +189,8 @@ void setBpm(float b)
 
 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)
        {
@@ -247,8 +243,7 @@ void incrCurrentFrame()
 
        if (c->status == ClockStatus::WAITING) {
                int f = currentFrameWait_.load() + 1;
-               if (f >= c->framesInLoop)
-                               f = 0;
+               f %= c->framesInLoop;
                currentFrameWait_.store(f);
                return;
        }
@@ -256,13 +251,8 @@ void incrCurrentFrame()
        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);
@@ -380,39 +370,43 @@ void sendMIDIrewind()
                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
@@ -426,7 +420,6 @@ bool canQuantize()
        model::ClockLock lock(model::clock);
        
        const model::Clock* c = model::clock.get();
-       
        return c->quantize > 0 && c->status == ClockStatus::RUNNING;
 }
 
@@ -434,16 +427,26 @@ bool canQuantize()
 /* -------------------------------------------------------------------------- */
 
 
-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::
index 670e4ce6b1c7ac960fc131217150eea705d9b20a..92d1d1beb3bb5262d3567ed2c28e193c44c82bfa 100644 (file)
@@ -32,9 +32,7 @@
 #include "types.h"
 
 
-namespace giada {
-namespace m {
-namespace clock
+namespace giada::m::clock
 {
 void init(int sampleRate, float midiTCfps);
 
@@ -53,7 +51,7 @@ Rewinds timecode to beat 0 and also send a MTC full frame to cue the slave. */
 
 void sendMIDIrewind();
 
-#if defined(__linux__) || defined(__FreeBSD__)
+#if defined(G_OS_LINUX) || defined(G_OS_FREEBSD) || defined(G_OS_MAC)
 void recvJackSync();
 #endif
 
@@ -66,25 +64,30 @@ int getFramesInBar();
 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);
@@ -106,7 +109,7 @@ bool isOnFirstBeat();
 
 void rewind();
 void setStatus(ClockStatus s);
-}}}; // giada::m::clock::
+} // giada::m::clock::
 
 
 #endif
index 7d8b000f1d31a77fb7d63d31841d13d8299e4139..769c9362e563b2835aae78dbb0e845685620844f 100644 (file)
@@ -53,6 +53,16 @@ std::string confDirPath_  = "";
 /* -------------------------------------------------------------------------- */
 
 
+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. */
@@ -81,8 +91,7 @@ int createConfigFolder_()
 
 #endif
 }
-
-}; // {anonymous}
+} // {anonymous}
 
 
 /* -------------------------------------------------------------------------- */
@@ -130,86 +139,89 @@ bool read()
 
        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;
 }
 
@@ -224,85 +236,86 @@ bool write()
 
        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_);
@@ -314,4 +327,4 @@ bool write()
     ofs << j;
        return true;
 }
-}}}; // giada::m::conf::
\ No newline at end of file
+}}} // giada::m::conf::
\ No newline at end of file
index c750c7b10c4c70dd6ba2a1652e8489c90a83c714..91ca9d3a2db51ab46a65bca8dd9fb59155d3ca44 100644 (file)
@@ -41,16 +41,17 @@ namespace conf
 {
 struct Conf
 {
-       int  logMode        = LOG_MODE_MUTE;
-       int  soundSystem    = G_DEFAULT_SOUNDSYS;
-       int  soundDeviceOut = G_DEFAULT_SOUNDDEV_OUT;
-       int  soundDeviceIn  = G_DEFAULT_SOUNDDEV_IN;
-       int  channelsOut    = 0;
-       int  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;
@@ -60,10 +61,10 @@ struct Conf
        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;
@@ -151,6 +152,6 @@ extern Conf conf;
 void init();
 bool read();
 bool write();
-}}}; // giada::m::conf::
+}}} // giada::m::conf::
 
 #endif
index 8547fbe5809d9dbf2e6e22921b32746884b9a26a..0e3ede2d4c830883d81ceb8da161bf99494d7f6a 100644 (file)
 #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";
 
@@ -65,8 +77,8 @@ 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;
@@ -88,28 +100,31 @@ constexpr int   G_GUI_ZOOM_FACTOR    = 2;
 
 
 /* -- 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;
 
 
 
@@ -143,28 +158,30 @@ constexpr int G_MIDI_API_ALSA = 0x02;  // 0000 0010
        #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
 
 
 
@@ -259,36 +276,27 @@ constexpr int G_MIDI_OUT_L_SOLO     = 5;
 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 */
 
@@ -344,6 +352,7 @@ constexpr auto PATCH_KEY_CHANNEL_HAS_ACTIONS          = "has_actions";
 constexpr auto PATCH_KEY_CHANNEL_READ_ACTIONS         = "read_actions";
 constexpr auto PATCH_KEY_CHANNEL_PITCH                = "pitch";
 constexpr auto PATCH_KEY_CHANNEL_INPUT_MONITOR        = "input_monitor";
+constexpr auto PATCH_KEY_CHANNEL_OVERDUB_PROTECTION   = "overdub_protection";
 constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_READ_ACTIONS = "midi_in_read_actions";
 constexpr auto PATCH_KEY_CHANNEL_MIDI_IN_PITCH        = "midi_in_pitch";
 constexpr auto PATCH_KEY_CHANNEL_MIDI_OUT             = "midi_out";
@@ -363,6 +372,7 @@ constexpr auto PATCH_KEY_PLUGIN_ID                    = "id";
 constexpr auto PATCH_KEY_PLUGIN_PATH                  = "path";
 constexpr auto PATCH_KEY_PLUGIN_BYPASS                = "bypass";
 constexpr auto PATCH_KEY_PLUGIN_PARAMS                = "params";
+constexpr auto PATCH_KEY_PLUGIN_STATE                 = "state";
 constexpr auto PATCH_KEY_PLUGIN_MIDI_IN_PARAMS        = "midi_in_params";
 constexpr auto PATCH_KEY_COLUMN_ID                    = "id";
 constexpr auto PATCH_KEY_COLUMN_WIDTH                 = "width";
@@ -376,85 +386,86 @@ constexpr auto G_PATCH_KEY_ACTION_NEXT                = "next";
 
 /* JSON config keys */
 
-constexpr auto CONF_KEY_HEADER                   = "header";
-constexpr auto CONF_KEY_LOG_MODE                 = "log_mode";
-constexpr auto CONF_KEY_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 */
 
index 74e09a3023bee401635c8ca668905c63b78db668..3a9e83a0d48e13ff88d3cedb054c843f2f29b8d9 100644 (file)
@@ -1855,3 +1855,31 @@ const char* armOn_xpm[] = {
 "                  ",
 "                  ",
 "                  "};
+
+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
index 3c2cb1d613438538e2ea183e921d5492e0cf8b31..7db77c9976e23cb87dddd8be8a0336f11532fbc0 100644 (file)
@@ -86,6 +86,7 @@ extern const char* soloOn_xpm[];
 
 extern const char* armOff_xpm[];
 extern const char* armOn_xpm[];
+extern const char* armDisabled_xpm[];
 
 extern const char* readActionOn_xpm[];
 extern const char* readActionOff_xpm[];
index 2c0f100669a6b688bda009e70488c0131dba26f0..8e8fa657098ec988dec5e18f8aea941d77da1169 100644 (file)
@@ -28,8 +28,7 @@
 #include "idManager.h"
 
 
-namespace giada {
-namespace m 
+namespace giada::m 
 {
 IdManager::IdManager() : m_id(0)
 {
@@ -53,4 +52,4 @@ ID IdManager::get(ID id)
 {
        return id != 0 ? id : ++m_id;
 }
-}} // giada::m::
+} // giada::m::
index 4b54b1a1771905043afbd82aec37f23c9b969da3..54042865c3584b29024ec987eacad6757f65d469 100644 (file)
 #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
index d43a5612907bd8ca50dccaa6dd5ae00af60796ff..9c15aa8c1c7e946d041e5ea3c817a3ae8a97a350 100644 (file)
 #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"
@@ -83,6 +84,8 @@ void initConf_()
        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");
@@ -100,6 +103,7 @@ void initAudio_()
        kernelAudio::openDevice();
        clock::init(conf::conf.samplerate, conf::conf.midiTCfps);
        mh::init();
+       sequencer::init();
        recorder::init();
        recorderHandler::init();
 
@@ -200,17 +204,23 @@ void printBuildInfo_()
 {
        u::log::print("[init] Giada %s\n", G_VERSION_STR);
        u::log::print("[init] Build date: " BUILD_DATE "\n");
+#ifdef G_DEBUG_MODE
+       u::log::print("[init] Debug build\n");
+#else
+       u::log::print("[init] Release build\n");
+#endif
        u::log::print("[init] Dependencies:\n");
        u::log::print("[init]   FLTK - %d.%d.%d\n", FL_MAJOR_VERSION, FL_MINOR_VERSION, FL_PATCH_VERSION);
-       u::log::print("[init]   RtAudio - %s\n", u::ver::getRtAudioVersion().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}
 
@@ -260,6 +270,7 @@ void reset()
        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());
@@ -278,6 +289,8 @@ void shutdown()
 {
        shutdownGUI_();
 
+       model::store(conf::conf);
+
        if (!conf::write())
                u::log::print("[init] error while saving configuration file!\n");
        else
index 4df1bd4a1408b480100021a15d4cb1e7b2201d0b..aa1ed50cbb39599e16980d46d38e045104206977 100644 (file)
 #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
 {
@@ -49,17 +48,17 @@ bool     inputEnabled = false;
 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}
 
 
 /* -------------------------------------------------------------------------- */
@@ -67,10 +66,22 @@ jack_client_t* jackGetHandle()
 /* -------------------------------------------------------------------------- */
 
 
+#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;
 }
 
@@ -125,7 +136,7 @@ int openDevice()
                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();
@@ -138,7 +149,7 @@ int openDevice()
        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;
@@ -148,12 +159,14 @@ int openDevice()
        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
@@ -165,34 +178,33 @@ int openDevice()
 
        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;
        }
@@ -210,7 +222,7 @@ int startStream()
                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;
        }
 }
@@ -225,7 +237,7 @@ int stopStream()
                rtSystem->stopStream();
                return 1;
        }
-       catch (RtAudioError &e) {
+       catch (RtAudioError& /*e*/) {
                u::log::print("[KA] Stop stream error\n");
                return 0;
        }
@@ -240,7 +252,7 @@ std::string getDeviceName(unsigned dev)
        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 "";
        }
@@ -272,7 +284,7 @@ unsigned getMaxInChans(int dev)
        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;
        }
@@ -287,7 +299,7 @@ unsigned getMaxOutChans(unsigned dev)
        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;
        }
@@ -302,7 +314,7 @@ bool isProbed(unsigned dev)
        try {
                return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).probed;
        }
-       catch (RtAudioError &e) {
+       catch (RtAudioError& /*e*/) {
                return 0;
        }
 }
@@ -316,7 +328,7 @@ unsigned getDuplexChans(unsigned dev)
        try {
                return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).duplexChannels;
        }
-       catch (RtAudioError &e) {
+       catch (RtAudioError& /*e*/) {
                return 0;
        }
 }
@@ -330,7 +342,7 @@ bool isDefaultIn(unsigned dev)
        try {
                return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).isDefaultInput;
        }
-       catch (RtAudioError &e) {
+       catch (RtAudioError& /*e*/) {
                return 0;
        }
 }
@@ -344,7 +356,7 @@ bool isDefaultOut(unsigned dev)
        try {
                return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).isDefaultOutput;
        }
-       catch (RtAudioError &e) {
+       catch (RtAudioError& /*e*/) {
                return 0;
        }
 }
@@ -358,7 +370,7 @@ int getTotalFreqs(unsigned dev)
        try {
                return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).sampleRates.size();
        }
-       catch (RtAudioError &e) {
+       catch (RtAudioError& /*e*/) {
                return 0;
        }
 }
@@ -372,7 +384,7 @@ int getFreq(unsigned dev, int i)
        try {
                return static_cast<RtAudio::DeviceInfo>(rtSystem->getDeviceInfo(dev)).sampleRates.at(i);
        }
-       catch (RtAudioError &e) {
+       catch (RtAudioError& /*e*/) {
                return 0;
        }
 }
@@ -432,19 +444,56 @@ int getAPI() { return api; }
 /* -------------------------------------------------------------------------- */
 
 
-#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
+       };
 }
 
 
@@ -454,7 +503,7 @@ const JackState &jackTransportQuery()
 void jackStart()
 {
        if (api == G_SYS_API_JACK)
-               jack_transport_start(jackGetHandle());
+               jack_transport_start(jackGetHandle_());
 }
 
 
@@ -466,9 +515,9 @@ void jackSetPosition(uint32_t frame)
        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);
 }
 
 
@@ -480,13 +529,13 @@ void jackSetBpm(double bpm)
        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);
 }
 
 
@@ -496,9 +545,8 @@ void jackSetBpm(double bpm)
 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
index 942a13967ff759cc748c88c54ab2bcac24fb5f6a..8e3ed7ea1e0b04b323ee7a62c8f5ddec26e66a13 100644 (file)
 
 
 #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
@@ -75,16 +75,18 @@ int getDefaultOut();
 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
index 93e8da24dbc802a7855e9af58590a42bd582207f..4e0f62b1958597204a967678c99cee43dd60987a 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#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"
@@ -51,7 +47,7 @@ unsigned numOutPorts_ = 0;
 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());
@@ -72,11 +68,12 @@ void sendMidiLightningInitMsgs_()
        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}
 
 
@@ -102,7 +99,7 @@ int openOutDevice(int port)
                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;
        }
@@ -112,7 +109,7 @@ int openOutDevice(int port)
        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 */
 
@@ -128,7 +125,7 @@ int openOutDevice(int port)
                        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;
                }
@@ -148,7 +145,7 @@ int openInDevice(int port)
                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;
        }
@@ -158,7 +155,7 @@ int openInDevice(int port)
        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 */
 
@@ -171,7 +168,7 @@ int openInDevice(int port)
                        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;
                }
@@ -201,13 +198,13 @@ bool hasAPI(int API)
 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 ""; }
 }
 
 
@@ -224,7 +221,7 @@ void send(uint32_t data)
        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]);
 }
 
 
@@ -244,30 +241,30 @@ void send(int b1, int b2, int b3)
                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. */
@@ -284,6 +281,7 @@ unsigned countInPorts()  { return numInPorts_; }
 unsigned countOutPorts() { return numOutPorts_; }
 bool getStatus()         { return status_; }
 
+
 /* -------------------------------------------------------------------------- */
 
 
@@ -296,15 +294,4 @@ uint32_t getIValue(int b1, int b2, int b3)
 {
        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::
index f185b0c1b1f6398e7dce877a60640f71d14016ec..7e1c90a6e75821c170191435fa5d36ddcc819108 100644 (file)
@@ -44,12 +44,6 @@ int getB3(uint32_t iValue);
 
 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. */
 
@@ -59,7 +53,7 @@ void send(int b1, int b2=-1, int b3=-1);
 /* 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. */
@@ -89,7 +83,7 @@ unsigned countOutPorts();
 
 bool hasAPI(int API);
 
-}}}; // giada::m::kernelMidi::
+}}} // giada::m::kernelMidi::
 
 
 #endif
index 09227fc28294a9de186f670507ae970f4d1f5b92..eb2bd2066def6108d5a63d1d1b38d368636a2911 100644 (file)
 #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"
@@ -79,7 +74,7 @@ bool isMasterMidiInAllowed_(int c)
 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);
 }
 
 
@@ -95,19 +90,18 @@ void processPlugins_(const std::vector<ID>& ids, const MidiEvent& midiEvent)
 
        /* 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);
                }
        }
 }
@@ -122,98 +116,68 @@ void processChannels_(const MidiEvent& midiEvent)
 {
        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);
+       }
 }
 
 
@@ -228,43 +192,43 @@ void processMaster_(const MidiEvent& midiEvent)
        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);
        }
 }
@@ -280,18 +244,21 @@ void learnChannel_(MidiEvent e, int param, ID channelId, std::function<void()> d
 
        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;
                }
        });
 
@@ -307,18 +274,18 @@ void learnMaster_(MidiEvent e, int param, std::function<void()> doneCb)
 
        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;
                }
        });
 
@@ -329,11 +296,12 @@ void learnMaster_(MidiEvent e, int param, std::function<void()> doneCb)
 
 #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();
@@ -375,7 +343,7 @@ void startMasterLearn (int param, std::function<void()> f)
 
 #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); };
 }
@@ -406,7 +374,7 @@ void clearChannelLearn(int param, ID channelId, std::function<void()> 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)
 }
@@ -455,5 +423,5 @@ void setSignalCallback(std::function<void()> f)
        signalCb_ = f;
 }
 
-}}}; // giada::m::midiDispatcher::
+}}} // giada::m::midiDispatcher::
 
index dd5de7cb182718287894ef029ac17d5e7ad33b93..45f79e817b2e9baedfff771cc8a73e8ccead6946 100644 (file)
@@ -46,14 +46,14 @@ void stopLearn();
 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
index abdbea4bd43a052fd690aabaeb92126d9e721873..a22d987f9c926ffd910e5b8e51e53e8fd4f3d398 100644 (file)
 
 #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
 {
 }
 
@@ -59,9 +58,20 @@ MidiEvent::MidiEvent(uint32_t raw)
 /* -------------------------------------------------------------------------- */
 
 
+/* 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);
 }
 
 
@@ -128,6 +138,12 @@ int MidiEvent::getVelocity() const
 }      
 
 
+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;
index 711c54d2a5e394acb4452b64491b65900a3ce4c8..eefeb6c8ffe36c6cf6afb8de9ea97090fd8c1c8f 100644 (file)
@@ -44,14 +44,25 @@ public:
        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;
 
diff --git a/src/core/midiLearnParam.cpp b/src/core/midiLearnParam.cpp
new file mode 100644 (file)
index 0000000..6fd9d47
--- /dev/null
@@ -0,0 +1,73 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/midiLearnParam.h b/src/core/midiLearnParam.h
new file mode 100644 (file)
index 0000000..c5a86a0
--- /dev/null
@@ -0,0 +1,57 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
index fa4efc7bdfc4818e4bb8df03e64e457d23594791..0a481728caabdae8b8fc1abc352833f1e7abb953 100644 (file)
@@ -29,7 +29,7 @@
 #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"
@@ -92,7 +92,7 @@ void parse_(Message& message)
 
        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, "");
 
@@ -116,10 +116,9 @@ void parse_(Message& message)
        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}
 
 
 /* -------------------------------------------------------------------------- */
@@ -142,30 +141,22 @@ void init()
        /* 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);
 }
 
 
@@ -197,7 +188,7 @@ int read(const std::string& file)
                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())
@@ -221,4 +212,4 @@ int read(const std::string& file)
 
        return MIDIMAP_READ_OK;
 }
-}}}; // giada::m::midimap::
+}}} // giada::m::midimap::
index 0d7ecc99990d5dead7907091036f36353702b430..4d348bdf996bd8051625125fe279baf910246dfe 100644 (file)
@@ -103,6 +103,6 @@ bool isDefined(const Message& msg);
 Reads a midi map from file 'file'. */
 
 int read(const std::string& file);
-}}}; // giada::m::midimap::
+}}} // giada::m::midimap::
 
 #endif
index ebe49660dd6d3cb99c1abd15fbaff1f3c8f78cfb..181b4527a7d5fa9886c3e660d3ea3781b08d00d0 100644 (file)
 #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"
 
 
@@ -54,56 +52,15 @@ namespace mixer
 {
 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. */
@@ -118,16 +75,25 @@ std::function<void()> signalCb_ = nullptr;
 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);
 }
 
 
@@ -140,151 +106,109 @@ void lineInRec_(const AudioBuffer& inBuf)
 {
        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);
 }
 
 
@@ -296,23 +220,20 @@ Cleans up every buffer. */
 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));
 }
 
 
@@ -320,22 +241,24 @@ void limitOutput_(AudioBuffer& outBuf)
 
 /* 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}
 
 
 /* -------------------------------------------------------------------------- */
@@ -343,26 +266,26 @@ void finalizeOutput_(AudioBuffer& outBuf)
 /* -------------------------------------------------------------------------- */
 
 
-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);
 }
 
 
@@ -388,21 +311,21 @@ void disable()
 /* -------------------------------------------------------------------------- */
 
 
-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_;
 }
 
 
@@ -410,24 +333,22 @@ const AudioBuffer& getVirtualInput()
 
 
 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. */
 
@@ -437,22 +358,28 @@ int masterPlay(void* outBuf, void* inBuf, unsigned bufferSize,
        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);
 
@@ -474,20 +401,6 @@ void close()
 /* -------------------------------------------------------------------------- */
 
 
-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. */
@@ -504,23 +417,17 @@ void stopInputRec()
 /* -------------------------------------------------------------------------- */
 
 
-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::
index 1ced3451976cdae16e97ec076331bfdb68d0caaf..3651af19af156f8ec61f727929a711afb6c8888d 100644 (file)
 #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 {
@@ -46,24 +49,57 @@ class AudioBuffer;
 
 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);
 
@@ -75,22 +111,22 @@ called. */
 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();
 
@@ -100,19 +136,21 @@ Core method (callback) */
 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::;
 
 
index 9f315054468e4c40edb0c245e8782731e5976a6c..a247020dd0edd142b69a3a1fe1e53494d9e70121 100644 (file)
 #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"
@@ -68,15 +65,14 @@ namespace
 {
 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;
 }
 
 
@@ -85,24 +81,15 @@ std::unique_ptr<Channel> createChannel_(ChannelType type, ID columnId, ID channe
 
 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);
@@ -112,31 +99,126 @@ bool channelHas_(std::function<bool(const Channel*)> 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}
 
 
 /* -------------------------------------------------------------------------- */
@@ -172,31 +254,9 @@ void close()
 /* -------------------------------------------------------------------------- */
 
 
-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));
 }
 
 
@@ -210,11 +270,20 @@ int loadChannel(ID channelId, const std::string& fname)
        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;
 }
 
@@ -233,10 +302,9 @@ int addAndLoadChannel(ID columnId, const std::string& fname)
 
 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. */
 
@@ -258,14 +326,14 @@ void cloneChannel(ID channelId)
        /* 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. */
@@ -279,22 +347,19 @@ void cloneChannel(ID channelId)
 
 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)); 
 }
 
@@ -304,8 +369,13 @@ void freeChannel(ID channelId)
 
 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();
 }
 
@@ -315,27 +385,22 @@ void freeAllChannels()
 
 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
@@ -349,69 +414,10 @@ void deleteChannel(ID channelId)
 
 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);
 }
 
 
@@ -420,9 +426,11 @@ void toggleSequencer()
 
 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;
+               });
        });
 }
 
@@ -430,24 +438,6 @@ void updateSoloCount()
 /* -------------------------------------------------------------------------- */
 
 
-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)
@@ -463,14 +453,14 @@ void setInToOut(bool v)
 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();
 }
 
 
@@ -482,114 +472,65 @@ bool getInToOut()
 
 /* -------------------------------------------------------------------------- */
 
-
-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::
index bbf84a9d6b3e83236f7a1339a73155e724112cc8..4e1cf0536fae0d4d23e5dbd8ab00665db21f4ec5 100644 (file)
@@ -57,7 +57,7 @@ void close();
 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. */
@@ -88,15 +88,7 @@ void cloneChannel(ID channelId);
 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. */
@@ -109,12 +101,6 @@ session. */
 
 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) */
 
@@ -125,11 +111,11 @@ True if 1 or more samples was edited via gEditor */
 
 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. */
index 2fff2e6c016ad24c4e6c937fe6da181510afb647..3de8e8c82a2383c7374a3bb1e9d52980cc1f3696 100644 (file)
@@ -27,9 +27,7 @@
 
 #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
 
@@ -44,7 +42,7 @@ RCUList<Kernel>   kernel(std::make_unique<Kernel>());
 RCUList<Recorder> recorder(std::make_unique<Recorder>());
 RCUList<MidiIn>   midiIn(std::make_unique<MidiIn>());
 RCUList<Actions>  actions(std::make_unique<Actions>());
-RCUList<Channel>  channels;
+RCUList<Channel> channels;
 RCUList<Wave>     waves;
 #ifdef WITH_VST
 RCUList<Plugin>   plugins;
@@ -60,7 +58,7 @@ Actions::Actions(const Actions& o) : map(o.map)
 }
 
 
-#ifndef NDEBUG
+#ifdef G_DEBUG_MODE
 
 void debug()
 {
@@ -78,14 +76,17 @@ 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
        }
@@ -94,7 +95,7 @@ void debug()
 
        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");
@@ -102,31 +103,31 @@ void debug()
        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::
index 15dd75f9b2ee32c7c45e55bfaf87799ee2fb9857..58396dc49e950b33287ae51077847a6aa839ef88 100644 (file)
 
 
 #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"
 
@@ -43,6 +44,44 @@ namespace giada {
 namespace m {
 namespace model
 {
+namespace
+{
+/* getIter_
+Returns an iterator of an element from list 'list' with given ID. */
+
+template<typename L>
+auto getIter_(L& list, ID id)
+{
+       static_assert(has_id<typename L::value_type>(), "This type has no ID");
+       auto it = std::find_if(list.begin(), list.end(), [&](auto* t)
+       {
+               return t->id == id;
+       });
+       assert(it != list.end());
+       return it;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+/* onSwapByIndex_
+Swaps i-th element from list with a new one and applies a function f to it. */
+
+template<typename L>
+void onSwapByIndex_(L& list, std::size_t i, std::function<void(typename L::value_type&)> f)
+{
+       std::unique_ptr<typename L::value_type> o = list.clone(i);
+       f(*o.get());
+       list.swap(std::move(o), i);
+}
+} // {anonymous}
+
+
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+/* -------------------------------------------------------------------------- */
+
+
 struct Clock
 {      
        ClockStatus status       = ClockStatus::STOPPED;
@@ -127,53 +166,45 @@ extern RCUList<Plugin>   plugins;
 #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);
@@ -188,26 +219,28 @@ template<typename L>
 typename L::value_type& get(L& list, ID id)
 {
        static_assert(has_id<typename L::value_type>(), "This type has no ID");
-       return **getIter(list, id);
+       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>
@@ -222,60 +255,18 @@ void onGet(L& list, std::function<void(typename L::value_type&)> f)
 /* ---------------------------------------------------------------------------*/ 
 
 
-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). */
 
@@ -287,10 +278,10 @@ void onSwap(L& list, std::function<void(typename L::value_type&)> f)
 }
 
 
-/* ---------------------------------------------------------------------------*/ 
+/* ---------------------------------------------------------------------------*/
 
 
-#ifndef NDEBUG
+#ifdef G_DEBUG_MODE
 
 void debug();
 
index dbbda735ca2ac96c3186be44956e48a418ebf340..c8f1a1929b1f37d438b32c75876c8179020c57e4 100644 (file)
 #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"
 
 
@@ -57,7 +56,8 @@ void store(patch::Patch& patch)
        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) 
@@ -111,22 +111,40 @@ void load(const patch::Patch& patch)
 
        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();
+       }
 }
 
 
@@ -138,16 +156,16 @@ void load(const conf::Conf& c)
        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::
diff --git a/src/core/model/traits.h b/src/core/model/traits.h
new file mode 100644 (file)
index 0000000..a371ae6
--- /dev/null
@@ -0,0 +1,51 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
index 671d7cf540a7ef210a99af7e36d13d46d077a913..14a688d4cf6b12876408a40cf5c31def74e7dbad 100644 (file)
@@ -87,8 +87,11 @@ void readPlugins_(const nl::json& j)
                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);                   
@@ -149,11 +152,11 @@ void readChannels_(const nl::json& j)
        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);
@@ -162,7 +165,7 @@ void readChannels_(const nl::json& j)
                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);
@@ -178,7 +181,7 @@ void readChannels_(const nl::json& j)
                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);
@@ -186,6 +189,7 @@ void readChannels_(const nl::json& j)
                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);
@@ -219,14 +223,11 @@ void writePlugins_(nl::json& j)
                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);
        }
@@ -290,7 +291,7 @@ void writeWaves_(nl::json& j)
 /* -------------------------------------------------------------------------- */
 
 
-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;
@@ -351,6 +352,7 @@ void writeChannels_(nl::json& j)
                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;
@@ -366,7 +368,37 @@ void writeChannels_(nl::json& j)
                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}
 
 
 /* -------------------------------------------------------------------------- */
@@ -407,11 +439,11 @@ void init()
 /* -------------------------------------------------------------------------- */
 
 
-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);
@@ -443,12 +475,12 @@ int read(const std::string& file, const std::string& basePath)
        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 {
@@ -460,6 +492,7 @@ int read(const std::string& file, const std::string& basePath)
                readWaves_(j, basePath);
                readActions_(j);
                readChannels_(j);
+               modernize_();
        }
        catch (nl::json::exception& e) {
                u::log::print("[patch::read] Exception thrown: %s\n", e.what());
@@ -468,4 +501,4 @@ int read(const std::string& file, const std::string& basePath)
 
        return G_PATCH_OK;
 }
-}}}; // giada::m::patch::
+}}} // giada::m::patch::
index 57ab057899e7a06996542e466dcbe19b80c5478c..0b4e32a802c07c6684670f8f7174cf36ddc1c69a 100644 (file)
@@ -69,7 +69,7 @@ struct Channel
        bool        mute;
        bool        solo;
        float       volume = G_DEFAULT_VOL;
-       float       pan    = 0.5f;
+       float       pan    = G_DEFAULT_PAN;
        bool        hasActions;
        bool        armed;
        bool        midiIn;
@@ -86,17 +86,18 @@ struct Channel
        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;
@@ -130,7 +131,8 @@ struct Plugin
        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
@@ -138,6 +140,7 @@ struct Plugin
 
 struct Patch
 {
+       Version     version;
        std::string name       = G_DEFAULT_PATCH_NAME;
        int         bars       = G_DEFAULT_BARS;
        int         beats      = G_DEFAULT_BEATS;
@@ -178,8 +181,8 @@ int read(const std::string& file, const std::string& basePath);
 /* 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
diff --git a/src/core/plugin.cpp b/src/core/plugin.cpp
deleted file mode 100644 (file)
index aa30bc3..0000000
+++ /dev/null
@@ -1,325 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
diff --git a/src/core/plugin.h b/src/core/plugin.h
deleted file mode 100644 (file)
index 898ea2d..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
diff --git a/src/core/pluginHost.cpp b/src/core/pluginHost.cpp
deleted file mode 100644 (file)
index 32644b4..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
diff --git a/src/core/pluginHost.h b/src/core/pluginHost.h
deleted file mode 100644 (file)
index c362562..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
diff --git a/src/core/pluginManager.cpp b/src/core/pluginManager.cpp
deleted file mode 100644 (file)
index 9746da9..0000000
+++ /dev/null
@@ -1,392 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
diff --git a/src/core/pluginManager.h b/src/core/pluginManager.h
deleted file mode 100644 (file)
index 8d65cb3..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
diff --git a/src/core/plugins/plugin.cpp b/src/core/plugins/plugin.cpp
new file mode 100644 (file)
index 0000000..df5909d
--- /dev/null
@@ -0,0 +1,387 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/plugins/plugin.h b/src/core/plugins/plugin.h
new file mode 100644 (file)
index 0000000..6ec9608
--- /dev/null
@@ -0,0 +1,144 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/plugins/pluginHost.cpp b/src/core/plugins/pluginHost.cpp
new file mode 100644 (file)
index 0000000..6271535
--- /dev/null
@@ -0,0 +1,267 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/plugins/pluginHost.h b/src/core/plugins/pluginHost.h
new file mode 100644 (file)
index 0000000..d75d21c
--- /dev/null
@@ -0,0 +1,99 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/plugins/pluginManager.cpp b/src/core/plugins/pluginManager.cpp
new file mode 100644 (file)
index 0000000..a1c6077
--- /dev/null
@@ -0,0 +1,360 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/plugins/pluginManager.h b/src/core/plugins/pluginManager.h
new file mode 100644 (file)
index 0000000..5bb19ac
--- /dev/null
@@ -0,0 +1,117 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/plugins/pluginState.cpp b/src/core/plugins/pluginState.cpp
new file mode 100644 (file)
index 0000000..97a257e
--- /dev/null
@@ -0,0 +1,74 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/plugins/pluginState.h b/src/core/plugins/pluginState.h
new file mode 100644 (file)
index 0000000..b30ddba
--- /dev/null
@@ -0,0 +1,60 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/quantizer.cpp b/src/core/quantizer.cpp
new file mode 100644 (file)
index 0000000..7be2fa1
--- /dev/null
@@ -0,0 +1,97 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/core/quantizer.h b/src/core/quantizer.h
new file mode 100644 (file)
index 0000000..e9973b7
--- /dev/null
@@ -0,0 +1,83 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
index 72c10eb2d1aaf9c165e0f62f3f1d4d617f877b2e..6edf1c55bc672fadd380657b924d04a24e7de3b5 100644 (file)
@@ -39,7 +39,7 @@ namespace m
 /* Queue
 Single producer, single consumer lock-free queue. */
 
-template<typename T, size_t size>
+template<typename T, std::size_t size>
 class Queue
 {
 public:
@@ -54,7 +54,7 @@ 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;
 
@@ -66,8 +66,8 @@ public:
 
     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;
@@ -79,15 +79,15 @@ public:
 
 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::
 
index ae48fb3932b76389115ce681c49e33c24ca813b3..96e91066f4f560c253bca4199b7d547cc8f32f43 100644 (file)
@@ -37,11 +37,6 @@ namespace giada
 template<typename T>
 class Range
 {
-private:
-
-       T m_a;
-       T m_b;
-
 public:
 
        Range() : m_a(0), m_b(0) {}
@@ -50,6 +45,11 @@ public:
        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::
 
index c5be2999f9c65306640ee0e647efd862df1514fd..00504890f96c3de6a1432414aba82d2c15052f25 100644 (file)
 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;
        };
@@ -176,14 +177,13 @@ public:
        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");
@@ -192,18 +192,14 @@ public:
 
        /* 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
        {
@@ -212,19 +208,13 @@ public:
        }
 
        /* 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
@@ -233,7 +223,7 @@ public:
        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. */
 
@@ -339,7 +329,7 @@ public:
        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. */
 
@@ -449,7 +439,7 @@ public:
        /* size
        Returns the number of nodes in the list. */
 
-       size_t size() const
+       std::size_t size() const
        {
                return m_size.load();
        }
@@ -467,9 +457,9 @@ public:
 
 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) {
@@ -482,7 +472,7 @@ private:
 
        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
index 29865b45adb3287d1ede90dfa1e2baf1f493b127..f03c50f1ff985e0fc7db75306564d8e209ce2ca1 100644 (file)
 
 
 #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"
@@ -72,7 +72,8 @@ bool startActionRec_()
        if (!kernelAudio::isReady())
                return false;
        clock::setStatus(ClockStatus::RUNNING);
-       m::mh::startSequencer();
+       sequencer::start();
+       m::conf::conf.recTriggerMode = RecTriggerMode::NORMAL;
        return true;
 }
 
@@ -82,10 +83,11 @@ bool startActionRec_()
 
 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}
@@ -147,7 +149,7 @@ void stopActionRec()
 
        if (clock::getStatus() == ClockStatus::WAITING) {
                clock::setStatus(ClockStatus::STOPPED);
-               m::midiDispatcher::setSignalCallback(nullptr);
+               midiDispatcher::setSignalCallback(nullptr);
                v::dispatcher::setSignalCallback(nullptr);
                return;
        }
@@ -156,13 +158,16 @@ void stopActionRec()
 
        /* 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);
                });
+       }
 }
 
 
@@ -181,13 +186,15 @@ void toggleActionRec(RecTriggerMode m)
 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();
index 92855d122b10f726fc0f892ba4c732c3a4cb17ad..ed55dae6fcbb48b54125ace4d973801ea99bdbf0 100644 (file)
@@ -30,7 +30,6 @@
 #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"
@@ -50,8 +49,8 @@ IdManager actionId_;
 
 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);
@@ -78,14 +77,31 @@ void removeIf_(std::function<bool(const Action&)> f)
 {
        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}
 
 
@@ -156,7 +172,8 @@ void updateKeyFrames(std::function<Frame(Frame old)> f)
 {
        std::unique_ptr<model::Actions> ma = model::actions.clone();
        
-       /* Remove all existing actions: let's start from scratch. */
+       /* Remove all existing actions: let's start from scratch. 
+       TODO - so why cloning it?! */
 
        ma->map.clear();
 
@@ -166,14 +183,14 @@ void updateKeyFrames(std::function<Frame(Frame old)> f)
        {
                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);
                }
        }
 
@@ -230,8 +247,8 @@ bool hasActions(ID channelId, int type)
 {
        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;
@@ -262,6 +279,11 @@ Action makeAction(const patch::Action& a)
 
 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 
@@ -280,26 +302,16 @@ Action rec(ID channelId, Frame frame, MidiEvent event)
 /* -------------------------------------------------------------------------- */
 
 
-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);
        });
 }
@@ -393,8 +405,17 @@ void forEachAction(std::function<void(const Action&)> f)
 {
        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::
index 6369f90d323cb14bbac1b950ae880a961ad3eaba..fc1e78802e55e08fba78a0301641b6cb5bb36071 100644 (file)
@@ -150,7 +150,13 @@ vector makes it reallocating the existing ones. Also needed in model::Data copy
 constructor. */
 
 void updateMapPointers(ActionMap& src); 
-}}}; // giada::m::recorder::
+
+/* getNewActionId
+Returns a new action ID, internally generated. */
+
+ID getNewActionId();
+
+}}} // giada::m::recorder::
 
 
 #endif
index 3b38d25248d0adfefb1e1a5819fd1395b1177f94..b35b922ad08e1d07f3c3665f031815f592db511d 100644 (file)
@@ -25,6 +25,7 @@
  * -------------------------------------------------------------------------- */
 
 
+#include <unordered_map>
 #include <algorithm>
 #include <cmath>
 #include <cassert>
@@ -55,8 +56,8 @@ std::vector<Action> recs_;
 
 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;
@@ -88,7 +89,7 @@ in linear sequence, the potential partner of 'a1' always lies beyond a1 itself.
 Without this trick (i.e. if it loops from vector.begin() each time) the
 algorithm would end up matching wrong partners. */
 
-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) {
 
@@ -149,7 +150,7 @@ void updateBpm(float oldval, float newval, int oldquanto)
                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)
@@ -181,17 +182,32 @@ bool cloneActions(ID channelId, ID newChannelId)
 {
        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;
@@ -201,14 +217,15 @@ bool cloneActions(ID channelId, ID newChannelId)
 /* -------------------------------------------------------------------------- */
 
 
-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));
 }
 
 
@@ -234,8 +251,13 @@ std::unordered_set<ID> consolidate()
 
 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();
 }
 
@@ -296,6 +318,6 @@ std::vector<patch::Action> serializeActions(const recorder::ActionMap& actions)
        return out;     
 }
 
-}}}; // giada::m::recorderHandler::
+}}} // giada::m::recorderHandler::
 
 
index 1a5691101810d264b9e166a9bdb650b9d652317b..533346fd2c3d2e12a15ac04f4af74aeca293499e 100644 (file)
@@ -68,7 +68,7 @@ bool cloneActions(ID channelId, ID newChannelId);
 /* liveRec
 Records a user-generated action. NOTE_ON or NOTE_OFF only for now. */
 
-void liveRec(ID channelId, MidiEvent e);
+void liveRec(ID channelId, MidiEvent e, Frame global);
 
 /* consolidate
 Records all live actions. Returns a set of channels IDs that have been 
@@ -86,7 +86,7 @@ Creates new Actions given the patch raw data and vice versa. */
 
 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
diff --git a/src/core/ringBuffer.h b/src/core/ringBuffer.h
new file mode 100644 (file)
index 0000000..c2520c3
--- /dev/null
@@ -0,0 +1,86 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
+
+
+
diff --git a/src/core/sequencer.cpp b/src/core/sequencer.cpp
new file mode 100644 (file)
index 0000000..c7bc031
--- /dev/null
@@ -0,0 +1,299 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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::
+
+
diff --git a/src/core/sequencer.h b/src/core/sequencer.h
new file mode 100644 (file)
index 0000000..19c8898
--- /dev/null
@@ -0,0 +1,61 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
index 9d628e3127935041b711a6355434079838e24330..ac04502b496f658ee3e22ef6986ae827fc0b9a5d 100644 (file)
@@ -35,7 +35,13 @@ using ID    = int;
 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 };
 
@@ -44,7 +50,7 @@ enum class ChannelStatus : int
        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
@@ -52,9 +58,8 @@ enum class ChannelMode : int
 
 enum class RecTriggerMode : int { NORMAL = 0, SIGNAL };
 
-enum class PreviewMode : int { NONE = 0, NORMAL, LOOP };
 enum class EventType : int { AUTO = 0, MANUAL };
-};
+}
 
 
 #endif
index 7e8f8c02179d63c705d1d43287d6f32e57dff345..f30c14a5aaddfc4cd103ba1949fd3b6e4fed5fbb 100644 (file)
@@ -143,24 +143,28 @@ void Wave::setEdited(bool e)  { m_edited = e; }
 /* -------------------------------------------------------------------------- */
 
 
-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); }
+
+
 /* -------------------------------------------------------------------------- */
 
 
@@ -168,5 +172,4 @@ void Wave::moveData(AudioBuffer& b)
 {
        buffer.moveData(b);
 }
-
-}}; // giada::m::
+}} // giada::m::
index 030c4cb61e810ed6c35255cbf3d905b6bc212e8b..d3697326b473b76f194be1914dfb316f05c17268 100644 (file)
@@ -79,10 +79,15 @@ public:
        
        /* 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);
 
@@ -97,7 +102,7 @@ private:
        bool m_edited;      // edited via editor
        std::string m_path; // E.g. /path/to/my/sample.wav
 };
-}}; // giada::m::
+}} // giada::m::
 
 
 #endif
index d2cb8d1137dfc86e38e7efaaab7ff7081349a12a..0fc0531e9c77c62b838508938b4cd04ac94d790d 100644 (file)
@@ -63,7 +63,7 @@ float getPeak_(const Wave& w, int a, int b)
        }
        return peak;
 }
-}; // {anonymous}
+} // {anonymous}
 
 
 /* -------------------------------------------------------------------------- */
@@ -71,12 +71,15 @@ float getPeak_(const Wave& w, int a, int b)
 /* -------------------------------------------------------------------------- */
 
 
-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++) {
@@ -215,7 +218,7 @@ void paste(const Wave& src, ID waveId, int a)
 /* -------------------------------------------------------------------------- */
 
 
-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);
 
@@ -224,7 +227,7 @@ void fade(ID waveId, int a, int b, int type)
 
        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
@@ -249,8 +252,8 @@ void smooth(ID waveId, int a, int b)
                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);
 }
 
 
@@ -290,4 +293,4 @@ void reverse(ID waveId, int a, int b)
        });
 }
 
-}}}; // giada::m::wfx::
+}}} // giada::m::wfx::
index 6e94d1ab572f5a1eaf03aed8b657f2c1595cab19..3de9b45104655593a616f6631220f7ef4ca2ac96 100644 (file)
 #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,
@@ -46,10 +51,10 @@ not yet added to the RCUList. */
 
 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);
@@ -61,9 +66,9 @@ Pastes Wave 'src' into Wave at 'waveIndex', starting from frame 'a'. */
 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. */
@@ -76,7 +81,7 @@ Flips Wave's data. */
 void reverse(ID waveId, int a, int b);
 
 void shift(ID waveId, int offset);
-}}}; // giada::m::wfx::
+}}} // giada::m::wfx::
 
 
 #endif
index f820c937a49d0eefc9e89e538510d8079f7fac66..d0b2248c5768faa8cc6b4e9f288f94e3ee760612 100644 (file)
@@ -68,7 +68,7 @@ int getBits_(const SF_INFO& header)
                return 64;
        return 0;
 }
-}; // {anonymous}
+} // {anonymous}
 
 
 /* -------------------------------------------------------------------------- */
@@ -85,10 +85,10 @@ void init()
 /* -------------------------------------------------------------------------- */
 
 
-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 };
        }
 
@@ -99,7 +99,7 @@ Result createFromFile(const std::string& path, ID id)
        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 };
        }
 
@@ -120,6 +120,13 @@ Result createFromFile(const std::string& path, ID id)
 
        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());
 
@@ -153,7 +160,7 @@ std::unique_ptr<Wave> createFromWave(const Wave& src, int a, int b)
 
        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);
@@ -165,9 +172,9 @@ std::unique_ptr<Wave> createFromWave(const Wave& src, int a, int b)
 /* -------------------------------------------------------------------------- */
 
 
-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;
 }
 
 
@@ -183,7 +190,7 @@ const patch::Wave serializeWave(const Wave& w)
 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());
@@ -223,7 +230,7 @@ int save(const Wave& w, const std::string& path)
        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;
        }
 
@@ -234,4 +241,4 @@ int save(const Wave& w, const std::string& path)
 
        return G_RES_OK;
 }
-}}}; // giada::m::waveManager
+}}} // giada::m::waveManager
index f8e7a6a77b32619802d723ff7cf92049a3a58e35..98d7afdc6178e0742d2af2a664e6b2bdfff6f4e1 100644 (file)
@@ -40,14 +40,14 @@ namespace m
 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. */
@@ -55,10 +55,11 @@ 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. */
@@ -74,9 +75,13 @@ std::unique_ptr<Wave> createFromWave(const Wave& src, int a, int b);
 /* (de)serializeWave
 Creates a new Wave given the patch raw data and vice versa. */
 
-std::unique_ptr<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
@@ -84,7 +89,7 @@ Writes Wave data to file 'path'. Only 'wav' format is supported for now. */
 
 int save(const Wave& w, const std::string& path);
 
-}}}; // giada::m::waveManager
+}}} // giada::m::waveManager
 
 
 #endif
index 4039c869b4ec794f7a198a253d7c2f1456ebd66e..337a4a396f493f7099f7f264d2a93cab835412c9 100644 (file)
@@ -2,12 +2,6 @@
 #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"
diff --git a/src/deps/rtaudio/RtAudio.cpp b/src/deps/rtaudio/RtAudio.cpp
deleted file mode 100644 (file)
index 1ac3976..0000000
+++ /dev/null
@@ -1,10675 +0,0 @@
-/************************************************************************/
-/*! \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( &currentRate );
-  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( &currentWritePointer, &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( &currentWritePointer, &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( &currentReadPointer, &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( &currentReadPointer, &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( &params );
-
-  // 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, &param);
-    }
-    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, &param);
-    }
-    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, &param);
-    }
-    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
-
diff --git a/src/deps/rtaudio/RtAudio.h b/src/deps/rtaudio/RtAudio.h
deleted file mode 100644 (file)
index 53537c0..0000000
+++ /dev/null
@@ -1,1209 +0,0 @@
-/************************************************************************/
-/*! \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
diff --git a/src/deps/rtaudio/include/asio.cpp b/src/deps/rtaudio/include/asio.cpp
deleted file mode 100644 (file)
index b241663..0000000
+++ /dev/null
@@ -1,257 +0,0 @@
-/*\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
diff --git a/src/deps/rtaudio/include/asio.h b/src/deps/rtaudio/include/asio.h
deleted file mode 100644 (file)
index c74199f..0000000
+++ /dev/null
@@ -1,1070 +0,0 @@
-//---------------------------------------------------------------------------------------------------\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
diff --git a/src/deps/rtaudio/include/asiodrivers.cpp b/src/deps/rtaudio/include/asiodrivers.cpp
deleted file mode 100644 (file)
index 5f56454..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-#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
diff --git a/src/deps/rtaudio/include/asiodrivers.h b/src/deps/rtaudio/include/asiodrivers.h
deleted file mode 100644 (file)
index 2ddf7ad..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#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
diff --git a/src/deps/rtaudio/include/asiodrvr.h b/src/deps/rtaudio/include/asiodrvr.h
deleted file mode 100644 (file)
index 663f75a..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*\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
diff --git a/src/deps/rtaudio/include/asiolist.cpp b/src/deps/rtaudio/include/asiolist.cpp
deleted file mode 100644 (file)
index e4c73c2..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-#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;
-}
-
-
diff --git a/src/deps/rtaudio/include/asiolist.h b/src/deps/rtaudio/include/asiolist.h
deleted file mode 100644 (file)
index 01c64f0..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-#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
diff --git a/src/deps/rtaudio/include/asiosys.h b/src/deps/rtaudio/include/asiosys.h
deleted file mode 100644 (file)
index 003cf1a..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-#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
diff --git a/src/deps/rtaudio/include/dsound.h b/src/deps/rtaudio/include/dsound.h
deleted file mode 100644 (file)
index cb19cca..0000000
+++ /dev/null
@@ -1,2369 +0,0 @@
-/*==========================================================================;
- *
- *  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
-
diff --git a/src/deps/rtaudio/include/functiondiscoverykeys_devpkey.h b/src/deps/rtaudio/include/functiondiscoverykeys_devpkey.h
deleted file mode 100644 (file)
index 854244d..0000000
+++ /dev/null
@@ -1,212 +0,0 @@
-#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
-
-
-
-
diff --git a/src/deps/rtaudio/include/ginclude.h b/src/deps/rtaudio/include/ginclude.h
deleted file mode 100644 (file)
index d6dda0d..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#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
diff --git a/src/deps/rtaudio/include/iasiodrv.h b/src/deps/rtaudio/include/iasiodrv.h
deleted file mode 100644 (file)
index 64d2dbb..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#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
diff --git a/src/deps/rtaudio/include/iasiothiscallresolver.cpp b/src/deps/rtaudio/include/iasiothiscallresolver.cpp
deleted file mode 100644 (file)
index 08c55ea..0000000
+++ /dev/null
@@ -1,572 +0,0 @@
-/*
-       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_ (&param1);                                        \
-    __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 = &param1f64; /* 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 */
-
diff --git a/src/deps/rtaudio/include/iasiothiscallresolver.h b/src/deps/rtaudio/include/iasiothiscallresolver.h
deleted file mode 100644 (file)
index 63e91ca..0000000
+++ /dev/null
@@ -1,202 +0,0 @@
-// ****************************************************************************\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
diff --git a/src/deps/rtaudio/include/soundcard.h b/src/deps/rtaudio/include/soundcard.h
deleted file mode 100644 (file)
index 2cf3a2c..0000000
+++ /dev/null
@@ -1,1878 +0,0 @@
-/*
- * 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_ */
index d771ba83b42bf2bff89c2c5807fb08ec0831b164..1dc4838e3f71f0f1df5e5eed615ccd7115a3820f 100644 (file)
@@ -1 +1 @@
-#define IDI_ICON1 101
\ No newline at end of file
+#define IDI_ICON1 101
index 79ddfc29ef4cbc6995aca1260a7c8c9fed973921..1af65a1cec5d8fa920fa55cd4bb72d7f74538dc7 100644 (file)
 
 #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"
 
 
@@ -63,6 +62,7 @@ void recordFirstEnvelopeAction_(ID channelId, Frame frame, int value)
 {
        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); 
@@ -96,6 +96,7 @@ void recordNonFirstEnvelopeAction_(ID channelId, Frame frame, int value)
        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);
 
@@ -109,14 +110,13 @@ void recordNonFirstEnvelopeAction_(ID channelId, Frame frame, int value)
 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}
 
 
 /* -------------------------------------------------------------------------- */
@@ -124,6 +124,43 @@ bool isSinglePressMode_(ID channelId)
 /* -------------------------------------------------------------------------- */
 
 
+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;
@@ -155,7 +192,6 @@ void recordMidiAction(ID channelId, int note, int velocity, Frame f1, Frame f2)
 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);
@@ -164,12 +200,7 @@ void deleteMidiAction(ID channelId, const m::Action& a)
        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
@@ -240,8 +271,9 @@ void deleteSampleAction(ID channelId, const m::Action& a)
        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);
 }
@@ -283,13 +315,15 @@ void deleteEnvelopeAction(ID channelId, const m::Action& a)
        /* Deleting a boundary action wipes out everything. If is volume, remember 
        to restore _i and _d members in channel. */
        /* TODO - move this to c::*/
+       /* TODO - FIX*/
        if (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());
        }
@@ -359,4 +393,4 @@ void updateVelocity(const m::Action& a, int value)
 
        mr::updateEvent(a.id, event);
 }
-}}}; // giada::c::actionEditor::
+}}} // giada::c::actionEditor::
index c1414c2399e4dbc71cb491172ec96f632b7b4181..cf07002801b99466a21f408fa119a6102ee8fdb7 100644 (file)
@@ -29,7 +29,9 @@
 #define G_GLUE_ACTION_EDITOR_H
 
 
+#include <optional>
 #include <vector>
+#include <string>
 #include "core/types.h"
 
 
@@ -37,13 +39,33 @@ namespace giada {
 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.  */
 
@@ -66,7 +88,7 @@ void updateSampleAction(ID channelId, const m::Action& a, int type,
 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
index 30eaac13439aab663316efc6ff6aadd21cb3b175..f781ac606a629e7cf285450ed4c030ca28c2ffe0 100644 (file)
 #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"
@@ -86,25 +84,108 @@ void printLoadError_(int res)
        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;
+}
+
+
 /* -------------------------------------------------------------------------- */
 
 
@@ -187,26 +268,11 @@ void freeChannel(ID channelId)
 /* -------------------------------------------------------------------------- */
 
 
-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);
        });
 }
 
@@ -214,140 +280,52 @@ void setInputMonitor(ID channelId, bool 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;
+       });     
 }
 
 
@@ -358,56 +336,4 @@ void setName(ID channelId, const std::string& name)
 {
        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::
index 00b298df45f01e0df2db20e1b6692003ab382154..c82d7e84542cab345ee68e99e3cfb24f14667960 100644 (file)
@@ -1,4 +1,4 @@
-    /* -----------------------------------------------------------------------------
+       /* -----------------------------------------------------------------------------
  *
  * 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"
 
 
@@ -38,10 +41,100 @@ namespace giada {
 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. */
 
@@ -50,7 +143,7 @@ void addChannel(ID columnId, ChannelType type);
 /* 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. */
@@ -73,38 +166,19 @@ Unloads the sample from a sample channel. */
 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
diff --git a/src/glue/events.cpp b/src/glue/events.cpp
new file mode 100644 (file)
index 0000000..48e1321
--- /dev/null
@@ -0,0 +1,303 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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::
diff --git a/src/glue/events.h b/src/glue/events.h
new file mode 100644 (file)
index 0000000..7c8ab7b
--- /dev/null
@@ -0,0 +1,87 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
index 60de69f6db0a9df1de5d09986f640c35199320c1..518ca3f2f62a6eb72acac46e98536d5e4e5d41f5 100644 (file)
@@ -40,9 +40,6 @@
 #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"
@@ -67,12 +64,10 @@ namespace io
 {
 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}
 
@@ -81,64 +76,228 @@ void refreshMidiWindows_()
 /* -------------------------------------------------------------------------- */
 
 
-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
@@ -150,31 +309,56 @@ void startPluginMidiLearn(int paramIndex, ID pluginId)
 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::
index 9c05b86a7f408cdb18a1dc8c77e2826c30572029..46cbd8903d441f81bcb2ef55eedd418eda229183 100644 (file)
@@ -43,26 +43,119 @@ class Channel;
 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
index 22da7093f4f74e8f0f75181eb231036813235ba6..8fbf735283578c612240c0536852890909fc4ecb 100644 (file)
@@ -38,7 +38,6 @@
 #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"
@@ -50,8 +49,8 @@
 #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"
 
 
@@ -78,8 +77,8 @@ void setBpm_(float current, std::string s)
 
        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. */
@@ -89,7 +88,7 @@ void setBpm_(float current, std::string s)
                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}
 
@@ -99,6 +98,82 @@ void setBpm_(float current, std::string s)
 /* -------------------------------------------------------------------------- */
 
 
+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. */
@@ -111,10 +186,10 @@ void setBpm(const char* v1, const char* v2)
        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
@@ -152,7 +227,7 @@ void setBeats(int beats, int bars)
                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
@@ -171,39 +246,9 @@ void quantize(int val)
 /* -------------------------------------------------------------------------- */
 
 
-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);
@@ -227,58 +272,34 @@ void clearAllActions()
 /* -------------------------------------------------------------------------- */
 
 
-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::
index 7802a7200ed1840bcd4bd29044fd030c8cef5874..98a72c3fb9b8a21cc273aa76d26febfafc5578f3 100644 (file)
 #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. */
 
@@ -45,46 +92,20 @@ void setBpm(float v);
 
 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
index 9afc65e3767d40d8e11009a3589dd11b12781042..5c8860957430e6d3aad1f0daed4bc377cda6f509 100644 (file)
 #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"
@@ -55,9 +54,103 @@ namespace giada {
 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);
@@ -79,11 +172,8 @@ void updatePluginEditor_(ID pluginId, bool gui)
        child->updateParameters(!gui);
        if (!gui) Fl::unlock();
 }
-} // {anonymous}
 
 
-/* -------------------------------------------------------------------------- */
-/* -------------------------------------------------------------------------- */
 /* -------------------------------------------------------------------------- */
 
 
@@ -121,17 +211,7 @@ void freePlugin(ID pluginId, ID channelId)
 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); 
 }
 
 
@@ -166,7 +246,7 @@ void setPluginPathCb(void* data)
        configWin->refreshVstPath();
 }
 
-}}}; // giada::c::plugin::
+}}} // giada::c::plugin::
 
 
 #endif
index 52d79a05ddb45435a87094bc3c3d1070f440e019..0c2711941e7275003e1f9a55a5fddab7ab4e8c13 100644 (file)
 #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
 {
@@ -45,16 +52,76 @@ class Channel;
 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
@@ -62,7 +129,7 @@ Callback attached to the DirBrowser for adding new Plug-in search paths in the
 configuration window. */
 
 void setPluginPathCb(void* data);
-}}}; // giada::c::plugin::
+}}} // giada::c::plugin::
 
 
 #endif
index aacfc5afd1e3e04a047ffb9907f1a1e0a9f40f63..4eab2b260ab6490514883178e5c61b97ec676abd 100644 (file)
@@ -30,8 +30,6 @@
 #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"
@@ -89,13 +87,9 @@ void clearStartStopActions(ID channelId)
 
 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)
index f48219e6c877f34a8fea0af8ecaada40e98ea186..12535b53bcb801c04beb10974587f21cad5b94fd 100644 (file)
@@ -27,6 +27,7 @@
 
 #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"
@@ -42,8 +43,6 @@
 #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"
@@ -68,6 +67,8 @@ A Wave used during cut/copy/paste operations. */
 
 std::unique_ptr<m::Wave> waveBuffer_;
 
+Frame previewTracker_ = 0;
+
 
 /* -------------------------------------------------------------------------- */
 
@@ -77,17 +78,76 @@ needed for the operation. */
 
 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}
 
 
 /* -------------------------------------------------------------------------- */
@@ -95,6 +155,39 @@ void resetBeginEnd_(ID channelId)
 /* -------------------------------------------------------------------------- */
 
 
+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));
@@ -108,12 +201,20 @@ v::gdSampleEditor* getSampleEditorWindow()
 
 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();
 }
 
@@ -125,6 +226,7 @@ void cut(ID channelId, ID waveId, int a, int b)
 {
        copy(waveId, a, b);
        m::wfx::cut(waveId, a, b);
+       updateWavePtr_(channelId, waveId);
        resetBeginEnd_(channelId);
 }
 
@@ -135,7 +237,6 @@ void cut(ID channelId, ID waveId, int a, int b)
 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);
 }
 
@@ -151,6 +252,7 @@ void paste(ID channelId, ID waveId, int a)
        }
 
        m::wfx::paste(*waveBuffer_, waveId, a);
+       updateWavePtr_(channelId, waveId);
 
        /* Shift begin/end points to keep the previous position. */
 
@@ -160,8 +262,8 @@ void paste(ID channelId, ID waveId, int a)
 
        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)
@@ -177,45 +279,50 @@ void paste(ID channelId, ID waveId, int a)
 /* -------------------------------------------------------------------------- */
 
 
-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);
 }
 
 
@@ -225,6 +332,7 @@ void normalizeHard(ID waveId, int a, int b)
 void trim(ID channelId, ID waveId, int a, int b)
 {
        m::wfx::trim(waveId, a, b);
+       updateWavePtr_(channelId, waveId);
        resetBeginEnd_(channelId);
 }
 
@@ -232,49 +340,58 @@ void trim(ID channelId, ID waveId, int a, int b)
 /* -------------------------------------------------------------------------- */
 
 
-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)
        {
@@ -301,24 +418,16 @@ void reload(ID channelId, ID waveId)
                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();
 }
 
@@ -329,17 +438,19 @@ void reload(ID channelId, ID waveId)
 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::
index 8efc3d4456d07ce295cf99094169e80479bced88..d398e7e6e97161681990d81763373a15052b742e 100644 (file)
 #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. */
 
@@ -46,28 +92,25 @@ void copy(ID waveId, int a, int b);
 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
index 30c190491fe5346014c6a1dd4d343f2d5f494d62..2005c57812157129f4ecda277d10955c47f79e2e 100644 (file)
 #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"
@@ -92,6 +89,7 @@ std::string makeUniqueWavePath_(const std::string& base, const m::Wave& w)
        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))
@@ -111,12 +109,12 @@ bool savePatch_(const std::string& path, const std::string& name)
        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;
 }
@@ -125,14 +123,15 @@ bool savePatch_(const std::string& path, const std::string& name)
 /* -------------------------------------------------------------------------- */
 
 
-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}
@@ -145,13 +144,13 @@ void saveWavesToProject_(const std::string& base)
 
 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
@@ -190,10 +189,10 @@ void loadProject(void* data)
        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. */
 
@@ -221,26 +220,26 @@ void loadProject(void* data)
 
 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);
 
@@ -257,7 +256,7 @@ void saveProject(void* data)
 
 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())
@@ -278,9 +277,10 @@ void loadSample(void* data)
 
 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.");
@@ -293,12 +293,12 @@ void saveSample(void* data)
                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);
 
@@ -306,9 +306,9 @@ void saveSample(void* data)
                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);
diff --git a/src/glue/transport.cpp b/src/glue/transport.cpp
deleted file mode 100644 (file)
index 905cffc..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
index 9eea179fa544ff4ab83aaa311a49626071cb94cc..6423433280c555d16590d63afc8da4dfc827a643 100644 (file)
@@ -61,10 +61,15 @@ gdAbout::gdAbout()
 #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"
@@ -93,7 +98,7 @@ gdAbout::gdAbout()
 /* -------------------------------------------------------------------------- */
 
 
-void gdAbout::cb_close(Fl_Widget* w, void* p) { ((gdAbout*)p)->cb_close(); }
+void gdAbout::cb_close(Fl_Widget* /*w*/, void* p) { ((gdAbout*)p)->cb_close(); }
 
 
 /* -------------------------------------------------------------------------- */
index 330e341dc4454df96a84d417545801282b8ec021..1d2c5810d3f91189f74ec2e920929bea86764912 100644 (file)
@@ -45,7 +45,7 @@ public:
 
     gdAbout();
 
-    static void cb_close(Fl_Widget* w, void* p);
+    static void cb_close(Fl_Widget* /*w*/, void* p);
     inline void cb_close();
 
 private:
index c147abb8a83ec6253beba8e92dd5d75ac43862ca..c4f81a9861a1c3bec7857a65474cbd459dae1fec 100644 (file)
 #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"
 
@@ -46,9 +47,9 @@ namespace giada {
 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;
 
@@ -78,17 +79,8 @@ gdBaseActionEditor::~gdBaseActionEditor()
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -198,12 +190,9 @@ void gdBaseActionEditor::prepareWindow()
 {
        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);
index b874aa9d9ca411514c87ffb751fe344df9d257d7..e92af912a31717cc5ae83a6239293fd87ebf4757 100644 (file)
 
 
 #include "core/types.h"
+#include "glue/actionEditor.h"
 #include "gui/dialogs/window.h"
 
 
-class geChoice;
 class geButton;
-class geScroll;
 
 
 namespace giada {
@@ -46,9 +45,9 @@ struct Action;
 }
 namespace v
 {
+class geChoice;
 class geGridTool;
-
-
+class geScrollPack;
 class gdBaseActionEditor : public gdWindow
 {
 public:
@@ -60,16 +59,15 @@ 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
@@ -81,14 +79,12 @@ protected:
        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. */
@@ -99,6 +95,8 @@ protected:
        void centerViewportOut();
 
        void prepareWindow();
+
+       c::actionEditor::Data m_data;
 };
 }} // giada::v::
 
index 5dc98c40183d0a93fc6f793afe97355cda8d5bdd..6b5ec7f5b64001180211ac3c2f83dd94d7f37324 100644 (file)
 
 
 #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"
@@ -47,39 +46,41 @@ namespace v
 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();
 }
@@ -90,12 +91,12 @@ gdMidiActionEditor::gdMidiActionEditor(ID channelId)
 
 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::
index e5b7a4424e5e01cae9cd296036af2c564dca01aa..6b47af2389c4166a0fcd773ca7f7175753f385b0 100644 (file)
 
 #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"
@@ -49,50 +50,53 @@ namespace v
 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();
 }
@@ -103,13 +107,8 @@ gdSampleActionEditor::gdSampleActionEditor(ID channelId)
 
 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;
 }
 
 
@@ -118,14 +117,14 @@ bool gdSampleActionEditor::canChangeActionType()
 
 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::
index 02d987eea0332c96f7d51de9653d6128f0085fec..6f47840bed10c5c097c64ec9779e65390cb15c79 100644 (file)
@@ -40,7 +40,6 @@ namespace v
 {
 class geSampleActionEditor;
 class geEnvelopeEditor;
-
 class gdSampleActionEditor : public gdBaseActionEditor
 {  
 public:
@@ -51,13 +50,13 @@ public:
 
 private:
 
+       bool canChangeActionType();
+
        geSampleActionEditor* m_ae;
     geResizerBar*         m_aer;
 
        geEnvelopeEditor*     m_ee;
     geResizerBar*         m_eer;
-
-       bool canChangeActionType();
 };
 }} // giada::v::
 
index 922707c10d1a85ee41a6f1d03b71a11f5bec47c3..19cf64f69894008e1afe6144337e6824b36c11e6 100644 (file)
@@ -76,7 +76,7 @@ gdBeatsInput::gdBeatsInput()
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
index 43ac19326024cc846ac47fe71aee298c544d44a3..0126c79f753482cb6ab4ca2959051a6403ce38ce 100644 (file)
@@ -48,7 +48,7 @@ public:
 
 private:
 
-       static void cb_update(Fl_Widget* w, void* p);
+       static void cb_update(Fl_Widget* /*w*/, void* p);
        void cb_update();
 
        geInput* beats;
index 1b5c734add8090d380a665c0edd58db6d7617ce1..cede7f56c6e55e742e9b070c5ebca664b1acbcd8 100644 (file)
@@ -79,7 +79,7 @@ gdBpmInput::gdBpmInput(const char* label)
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
index a84a6a180e37e0a8b0b3f26be76b08bbed186b7f..e9465d1f094050057dbfb34c9f0cb0e3b2724fbe 100644 (file)
@@ -47,7 +47,7 @@ public:
 
 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;
index c575605110ca493776d32fec379d8f498dfc413b..81928d029603b07c78f01b5de32b8ad41158bab9 100644 (file)
@@ -110,9 +110,9 @@ gdBrowserBase::~gdBrowserBase()
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
index 2b95be5241d4dc17885acff9c3618a6e06989530..83a1b275058cc1b86413d2fba8e1c3baaa3e20dd 100644 (file)
@@ -77,9 +77,9 @@ protected:
        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();
index faee8ce8ffda1ee1b714b2d3232a96b747fa1a28..4ba2f455139d1e757d479b7e4a601798e8dc6c8f 100644 (file)
@@ -57,8 +57,8 @@ gdBrowserDir::gdBrowserDir(const std::string& title, const std::string& path,
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
index 395f2ca830ebcfece8bd246438919b9816134dd2..77b9ecc97aee7806d5dd741cc87dcb8baa397613 100644 (file)
@@ -44,8 +44,8 @@ public:
 
 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();
 };
index 1604a5b3c15ed8ef3bdf09292dcd127a23abc182..211d3f2bfe119e359fe1b6069174b5c7b111c895 100644 (file)
@@ -57,8 +57,8 @@ gdBrowserLoad::gdBrowserLoad(const std::string& title, const std::string& path,
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
index 71f42fa9a8e069ece6dcd3d55d8d0ca1d0e09aa9..39dfef57495bc3f176def60468a9750054f5d7e5 100644 (file)
@@ -48,8 +48,8 @@ public:
 
 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();
 };
index ca8bb2666d10cf81b33a373381317ce8d0e85e46..2462672e870b712d76a629c8b080c2293632aa1b 100644 (file)
@@ -62,8 +62,8 @@ gdBrowserSave::gdBrowserSave(const std::string& title, const std::string& path,
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
index f8fa6ced4b90a9df3cbd2d95409eef5df3956342..f6a88420e33103f0facdea945f922c64d52ab180 100644 (file)
@@ -56,8 +56,8 @@ private:
 
     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();
 };
index 764e267787789cc9141f092c24417fd95a297d0b..5bad36660173f33f0f9b03e48f92036a848baaf3 100644 (file)
@@ -27,7 +27,6 @@
 
 #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"
@@ -40,9 +39,9 @@
 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();
 
@@ -51,10 +50,7 @@ gdChannelNameInput::gdChannelNameInput(ID channelId)
        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);
@@ -70,8 +66,8 @@ gdChannelNameInput::gdChannelNameInput(ID channelId)
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -88,7 +84,7 @@ void gdChannelNameInput::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();
 }
 
index 1da6c7f9fcf3f9b4d6a78d86756599880ef4a706..7f3616720dc35c97be64d56826a6a981cb6a3e15 100644 (file)
@@ -43,16 +43,16 @@ class gdChannelNameInput : public gdWindow
 {
 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;
index b8fb27b8d7deb6c6b271cf3908bb28327badb0af..8ade6b2423e133689a9dbc80887fb572fa303f07 100644 (file)
@@ -77,8 +77,8 @@ gdConfig::gdConfig(int w, int h)
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
index a4a94f92b9923ea1b34fe0d2923bb2ee317a3fb1..003b3d26ca8ffd39c1f2f4c23629c7034107252c 100644 (file)
@@ -40,7 +40,6 @@ class geTabMisc;
 class geTabPlugins;
 #endif
 class geButton;
-class geChoice;
 class geCheck;
 class geInput;
 class geRadio;
@@ -50,6 +49,7 @@ class geBox;
 namespace giada {
 namespace v 
 {
+class geChoice;
 class gdConfig : public gdWindow
 {
 public:
@@ -72,8 +72,8 @@ 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();
 };
index 7457f91e32de416dd86134c6fb618ad2a6ba18a1..8d693a76450df9adebd56dc3e310629da5284c56 100644 (file)
@@ -30,9 +30,6 @@
 #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"
@@ -50,9 +47,9 @@ extern giada::v::gdMainWindow* mainWin;
 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, "");
@@ -63,7 +60,7 @@ gdKeyGrabber::gdKeyGrabber(ID channelId)
        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();
@@ -76,18 +73,15 @@ gdKeyGrabber::gdKeyGrabber(ID channelId)
 
 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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -114,7 +108,7 @@ void gdKeyGrabber::cb_clear()
 
 void gdKeyGrabber::setButtonLabel(int key)
 {
-       c::io::setSampleChannelKey(m_channelId, key);
+       c::io::channel_setKey(m_data.id, key);
 }
 
 /* -------------------------------------------------------------------------- */
@@ -124,7 +118,7 @@ void gdKeyGrabber::updateText(int 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());
@@ -148,7 +142,7 @@ int gdKeyGrabber::handle(int e)
                            && 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;
index a258fae3845c9df4b4dd9fe7ae372c21f6b42c53..edb7d6934963839a177927a7cbefb15336a3409e 100644 (file)
@@ -38,28 +38,33 @@ class geButton;
 
 
 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;
index 5ad9e149c512e57ae06a8602f0e14bacab9e66b7..fb1884d32541fabf493020b734dcca28cd944a98 100644 (file)
@@ -63,12 +63,12 @@ gdMainWindow::gdMainWindow(int W, int H, const char* title, int argc, char** arg
 
        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);
 
@@ -100,7 +100,7 @@ gdMainWindow::gdMainWindow(int W, int H, const char* title, int argc, char** arg
        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);
index 14d78dc6c6339772c0f8d947897110ba212749cd..7817d5279e1986da635c548fbcba5e54a8f45aec 100644 (file)
@@ -56,17 +56,7 @@ gdMidiInputBase::~gdMidiInputBase()
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
index 068e78b94d98460694143c2d6b0d66c8c84c4b7f..8264fb18e646004547abb18d7253d3a52fac3fd0 100644 (file)
 
 
 #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;
index 32d85df766ec4d3f9bd74164aa1827f71953f56b..f19bae4f9d64bec3f2b3d9503c7c826bb4992a84 100644 (file)
 #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");
@@ -127,15 +188,18 @@ gdMidiInputChannel::gdMidiInputChannel(ID channelId)
        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();
 }
 
@@ -143,73 +207,46 @@ gdMidiInputChannel::gdMidiInputChannel(ID channelId)
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -217,15 +254,7 @@ void gdMidiInputChannel::cb_veloAsVol(Fl_Widget* w, void* p) { ((gdMidiInputChan
 
 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());
 }
 
 
@@ -234,10 +263,7 @@ void gdMidiInputChannel::cb_enable()
 
 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());
 }
 
 
@@ -246,10 +272,7 @@ void gdMidiInputChannel::cb_veloAsVol()
 
 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::
index c6f520d3d9077a446af822eb63db47fe138a4fc7..65eb3d3f32478f9e68ddc62dc4a725e23ad3ec69 100644 (file)
 #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::
 
index 0ed0c7b8c8a565ce9923930ca1a4df65857e8135..23e682dc0948913a71696c74e76129edb2ecef9d 100644 (file)
 #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)");
@@ -101,14 +122,12 @@ gdMidiInputMaster::gdMidiInputMaster()
        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();
 }
 
@@ -116,8 +135,23 @@ gdMidiInputMaster::gdMidiInputMaster()
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -125,15 +159,7 @@ void gdMidiInputMaster::cb_setChannel(Fl_Widget* w, void* p) { ((gdMidiInputMast
 
 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());
 }
 
 
@@ -142,9 +168,6 @@ void gdMidiInputMaster::cb_enable()
 
 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::
index 7d0ef9688168b1a4d2d10b97105a1be100f908f5..216d4549a79a3cc109d3f16789a46c4d6a7550f3 100644 (file)
 #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::
 
index b559215c85a16a5f8e64ac3d3b27407eb566b99e..d5f750c144ae61f86ce879d9dfd914c9d60ff138 100644 (file)
@@ -26,6 +26,7 @@
 
 
 #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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -80,23 +101,16 @@ void gdMidiOutputBase::cb_close()
 
 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::
index de95cf154387a0063626037e5c8a811ba007be7f..884709941394d49605ec2f41dccf24000297be56 100644 (file)
 
 
 #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;
@@ -45,13 +47,22 @@ only with channels.
 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:
@@ -59,30 +70,29 @@ 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::
 
index 0ccf7f1d6fdd392014b16d451cf1752ddb51e92c..a4c7715269c39be2804fa38b610082e2bc0e223a 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#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"
 
 
@@ -41,26 +41,25 @@ namespace v
 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");
@@ -80,24 +79,15 @@ gdMidiOutputMidiCh::gdMidiOutputMidiCh(ID channelId)
        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();
 }
 
@@ -105,8 +95,25 @@ gdMidiOutputMidiCh::gdMidiOutputMidiCh(ID channelId)
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -114,13 +121,7 @@ void gdMidiOutputMidiCh::cb_setChannel(Fl_Widget *w, void *p) { ((gdMidiOutputMi
 
 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());
 }
 
 
@@ -129,9 +130,6 @@ void gdMidiOutputMidiCh::cb_enableOut()
 
 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::
index 41799fdf6a4cfd38606db8ae7be390c69fcb1ea3..0d6b6993055e410b232b8fa15d1ae9ed938194a0 100644 (file)
@@ -43,10 +43,12 @@ public:
 
        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();
 
index dee27dc32116636b6517a314e4d4d044b436a870..257d46ac35c5d6b8f1ec6da1e4ddea29a204d9d6 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
+#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"
@@ -40,30 +40,39 @@ namespace v
 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::
index 0dd41f599b3870f2ea78c307e945c5cbeda919d7..10f403310ba6406cc56c40c047505adbf81cca44 100644 (file)
@@ -40,6 +40,8 @@ class gdMidiOutputSampleCh : public gdMidiOutputBase
 public:
 
     gdMidiOutputSampleCh(ID channelId);
+
+       void rebuild() override;
 };
 }} // giada::v::
 
index 277604bab53e061ea276e01834a70ef2cd0b54e2..d3a209d41b3de0fc66214d869bc94585f38fca94 100644 (file)
 
 #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"
@@ -44,9 +43,9 @@
 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);
@@ -100,9 +99,9 @@ gdPluginChooser::~gdPluginChooser()
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -132,7 +131,7 @@ void gdPluginChooser::cb_add()
        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::
index beee9676cdfd6b22b386ebb3290768a2a859db2c..bdbb2b82345748a4dcb58c596a42f68da52c33e5 100644 (file)
 #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 ();
@@ -66,7 +66,7 @@ private:
        geButton*        cancelBtn;
        gePluginBrowser* browser;
 
-       ID m_chanID;
+       ID m_channelId;
 };
 }} // giada::v::
 
index f062261d76cdec995af390a801963936af4860e5..7e9115bbbf76e5fb72b3566f4a03771ea1cfdd88 100644 (file)
 
 #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"
@@ -55,9 +52,9 @@ extern giada::v::gdMainWindow* G_MainWin;
 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();
 
@@ -65,21 +62,11 @@ gdPluginList::gdPluginList(ID chanID)
                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();
 }
 
@@ -97,7 +84,7 @@ gdPluginList::~gdPluginList()
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -105,17 +92,25 @@ void gdPluginList::cb_addPlugin(Fl_Widget* v, void* p) { ((gdPluginList*)p)->cb_
 
 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 --"));
        
@@ -133,7 +128,7 @@ void gdPluginList::cb_addPlugin()
        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);
 }
 
 
index 8ac8f464834992a68fc0eb171a2ae4274d9e9da9..d0e027189028ea4439b8d25d59614af97b32f759 100644 (file)
@@ -32,7 +32,7 @@
 #define GD_PLUGINLIST_H
 
 
-#include "core/pluginHost.h"
+#include "glue/plugin.h"
 #include "window.h"
 
 
@@ -48,7 +48,7 @@ class gdPluginList : public gdWindow
 {
 public:
 
-       gdPluginList(ID chanID);
+       gdPluginList(ID channelId);
        ~gdPluginList();
 
        void rebuild() override;
@@ -58,15 +58,15 @@ public:
 
 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::
 
 
index 9e038d6e8aeee68a1869f392c346648681d7fd69..f21017331d93d8d1068b9f7a9533d1f3e44f34c3 100644 (file)
@@ -29,9 +29,8 @@
 
 
 #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);
@@ -79,40 +74,10 @@ gdPluginWindow::gdPluginWindow(ID pluginId)
 /* -------------------------------------------------------------------------- */
 
 
-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::
 
index 3c37c954bf4f6e90ca385ba5e1f18b5880f79195..a0c106f071f03b03e5f1ed641f2428ec3cb1acf0 100644 (file)
@@ -41,6 +41,11 @@ class geLiquidScroll;
 
 
 namespace giada {
+namespace c {
+namespace plugin
+{
+class Plugin;
+}}
 namespace m
 {
 class Plugin;
@@ -51,16 +56,13 @@ class gdPluginWindow : public gdWindow
 {
 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;
 };
index aa7c581de831a4805c5f91039ad939b4e63a041e..bd9091132cc3a575feb74986a53571a5870598cd 100644 (file)
@@ -31,9 +31,7 @@
 #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();
 
@@ -88,15 +86,18 @@ gdPluginWindowGUI::gdPluginWindowGUI(ID pluginId)
 
        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());
 }
 
 
@@ -112,7 +113,7 @@ gdPluginWindowGUI::~gdPluginWindowGUI()
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
@@ -121,7 +122,9 @@ void gdPluginWindowGUI::cb_refresh(void* data) { ((gdPluginWindowGUI*)data)->cb_
 
 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);
 }
@@ -142,10 +145,7 @@ void gdPluginWindowGUI::cb_refresh()
 
 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;
@@ -163,7 +163,6 @@ void gdPluginWindowGUI::closeEditor()
        delete m_ui;
        m_ui = nullptr;
 }
-
 }} // giada::v::
 
 
index 45e368207e5e9a38849f10c48adad7edeebbc5b5..211892abf9c909144f6cb362c39629f6dc421957 100644 (file)
 
 
 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();
@@ -60,7 +65,7 @@ private:
        void openEditor(void* parent); 
        void closeEditor(); 
 
-       ID m_pluginId;
+       c::plugin::Plugin& m_plugin;
 
        juce::AudioProcessorEditor* m_ui;
 };
index 93884ded0e8115bc56d501b8b4ea97e8fa7ad49b..a0362821dc634ea8ef25aac50e5c1bece5e6c8f9 100644 (file)
@@ -31,8 +31,6 @@
 #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"
@@ -48,6 +46,8 @@
 #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);
@@ -107,7 +106,8 @@ gdSampleEditor::~gdSampleEditor()
        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();
 }
 
 
@@ -116,24 +116,21 @@ gdSampleEditor::~gdSampleEditor()
 
 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();   
 }
 
 
@@ -143,28 +140,23 @@ void gdSampleEditor::rebuild()
 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");
@@ -189,29 +181,36 @@ Fl_Group* gdSampleEditor::createUpperBar()
        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;
 }
@@ -220,33 +219,19 @@ Fl_Group* gdSampleEditor::createOpTools(int x, int y, int h)
 /* -------------------------------------------------------------------------- */
 
 
-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;
 }
@@ -255,23 +240,25 @@ Fl_Group* gdSampleEditor::createInfoBox(int x, int y, int h)
 /* -------------------------------------------------------------------------- */
 
 
-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;
@@ -281,13 +268,13 @@ Fl_Group* gdSampleEditor::createBottomBar(int x, int y, int h)
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -304,16 +291,16 @@ void gdSampleEditor::cb_enableSnap()
 
 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);
 }
 
 
@@ -322,7 +309,7 @@ void gdSampleEditor::cb_rewindPreview()
 
 void gdSampleEditor::cb_reload()
 {
-       c::sampleEditor::reload(m_channelId, m_waveId);
+       c::sampleEditor::reload(m_data.channelId, m_data.waveId);
        redraw();
 }
 
@@ -359,27 +346,16 @@ void gdSampleEditor::cb_changeGrid()
 /* -------------------------------------------------------------------------- */
 
 
-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::
index f31c21d6868fc9863136f88f0c692ff0c2192c48..02c25bec8755c9fc81b1739967b22f20fcb50c6d 100644 (file)
 
 
 #include "core/types.h"
+#include "glue/sampleEditor.h"
 #include "window.h"
 
 
 class geButton;
-class geChoice;
 class geCheck;
 class geBox;
 class geButton;
@@ -44,11 +44,13 @@ class geStatusButton;
 namespace giada {
 namespace m
 {
-class SampleChannel;
 class Wave;
 }
 namespace v 
 {
+class geChoice;
+class gePack;
+class geGroup;
 class geVolumeTool;
 class geWaveTools;
 class geBoostTool;
@@ -56,22 +58,18 @@ class gePanTool;
 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;
@@ -97,20 +95,18 @@ public:
 
 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();
@@ -118,9 +114,12 @@ private:
        void cb_enableSnap();
        void cb_togglePreview();
        void cb_rewindPreview();
-       
+
+       void updateInfo();
+
        ID m_channelId;
-       ID m_waveId;
+
+       c::sampleEditor::Data m_data;
 };
 }} // giada::v::
 
index f6a644be4990922ee2ec3d00af27b96403909b01..7cc1d54fdf6a1396cffb18205d7e84f284c2bc8f 100644 (file)
@@ -32,7 +32,7 @@
 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;
 }
@@ -63,8 +63,8 @@ gdWindow::~gdWindow()
 {
        /* 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();
 }
 
@@ -74,9 +74,9 @@ gdWindow::~gdWindow()
 /* 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);
 }
@@ -87,15 +87,6 @@ void gdWindow::cb_closeChild(Fl_Widget* v, void* p)
 
 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);
@@ -108,30 +99,26 @@ void gdWindow::addSubWindow(gdWindow* 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();
 }
 
 
@@ -144,9 +131,9 @@ int gdWindow::getId() const
 }
 
 
-void gdWindow::setId(int id)
+void gdWindow::setId(int wid)
 {
-       this->id = id;
+       id = wid;
 }
 
 
@@ -155,10 +142,12 @@ void gdWindow::setId(int id)
 
 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");
+       */
 }
 
 
@@ -180,10 +169,10 @@ void gdWindow::setParent(gdWindow* w)
 /* -------------------------------------------------------------------------- */
 
 
-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;
 }
@@ -192,11 +181,11 @@ bool gdWindow::hasWindow(int id) const
 /* -------------------------------------------------------------------------- */
 
 
-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
index 54d1d814266613369e2d1c6ef83fd8b45f682e91..1923c91a1f5c4fc2efac61824e89a06c92795edb 100644 (file)
@@ -39,7 +39,7 @@ namespace v
 /* 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
 {
@@ -49,7 +49,7 @@ public:
        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
index 63f8aa40ac69f75af505294c71bf92090ba90579..c84dd8fac2d5c8b950ebed779349a7ed4693a49d 100644 (file)
 
 #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"
@@ -65,13 +58,20 @@ std::function<void()> signalCb_ = nullptr;
 /* -------------------------------------------------------------------------- */
 
 
-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);
 }
 
 
@@ -86,7 +86,7 @@ void dispatchChannels_(int event)
        G_MainWin->keyboard->forEachChannel([=](geChannel& c)
        {
                if (c.handleKey(event))
-                       perform_(&c, event);
+                       perform_(c.getData().id, event);
        });
 }
 
@@ -112,24 +112,24 @@ void triggerSignalCb_()
 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;
@@ -163,10 +163,10 @@ void dispatchKey(int event)
 /* -------------------------------------------------------------------------- */
 
 
-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);
 }
 
 
index e1670faa9bc743a1265e85d5f5b735cb0b77f668..bb10117e3acfa1f3d46d4dbe5a58543a5282f408 100644 (file)
@@ -47,7 +47,7 @@ void dispatchKey(int event);
 /* 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
index 73c0d2cde1dbdec20d59aadf0d96657540b323ad..af71efc96240859f6e78f09d2ba9474281f13067 100644 (file)
@@ -35,15 +35,15 @@ namespace v
 {
 geBaseAction::geBaseAction(Pixel X, Pixel Y, Pixel W, Pixel H, bool resizable,
        m::Action a1, m::Action a2)
-: Fl_Box     (X, Y, W, H),
-  m_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());
index 65f5007e95441b9b9cc658f88884b308246f17a5..ee5cf1b756e3f82e3247f1e4e1adffb467b1eac8 100644 (file)
 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)
 {
 }
 
@@ -51,7 +53,7 @@ geBaseActionEditor::geBaseActionEditor(Pixel x, Pixel y, Pixel w, Pixel h)
 
 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;
index e9163b0a7b1caf853f73ed7c84634285157ff244..74032d9734a3cd7dc0295dde9122c7532ae4440b 100644 (file)
@@ -43,12 +43,10 @@ class geBaseActionEditor : public Fl_Group
 {
 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. */
@@ -63,6 +61,13 @@ public:
 
 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
index aabfd2bd9f4675580b398d0df1fef17ae9c2a832..83fe8ec0f9795a1ee9548551d814d82ee837fd1b 100644 (file)
 #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"
@@ -44,8 +44,8 @@
 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);
 }
@@ -108,10 +108,9 @@ void geEnvelopeEditor::draw()
 /* -------------------------------------------------------------------------- */
 
 
-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. */
@@ -119,7 +118,7 @@ void geEnvelopeEditor::rebuild()
        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));                
@@ -175,9 +174,9 @@ void geEnvelopeEditor::onAddAction()
        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
 }
 
 
@@ -186,9 +185,9 @@ void geEnvelopeEditor::onAddAction()
 
 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
 }
 
 
@@ -225,7 +224,7 @@ void geEnvelopeEditor::onRefreshAction()
 {
        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();
 }
index b1e559150343a8cce7779a0a12acd5e87a7cff7a..50def75e34f07dddd70f3d5f4a14f121d77bccba 100644 (file)
@@ -40,18 +40,16 @@ class SampleChannel;
 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:
 
index c03d3d3eba4867fff6f75718f16ec7946138ad8b..95751a567f933006e1e1a19e6e84479d685b717b 100644 (file)
@@ -74,7 +74,7 @@ geGridTool::~geGridTool()
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
index d96c1af6cf007f04dfb57b0bb5442fd050748ca4..0b61bfcbe238b766fb3b8db1cb8934e9d9d3573a 100644 (file)
 #include "core/types.h"
 
 
-class geChoice;
 class geCheck;
 
 
 namespace giada {
 namespace v
 {
+class geChoice;
 class geGridTool : public Fl_Group
 {
 public:
@@ -62,7 +62,7 @@ private:
        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::
index 784833f634c6666850a3f29974ae51efaed18088..574280f4cf1f6e34c272df7884eea70dc0aefa34 100644 (file)
@@ -26,7 +26,6 @@
 
 
 #include <FL/Fl.H>
-#include "core/channels/midiChannel.h"
 #include "core/const.h"
 #include "core/conf.h"
 #include "gui/dialogs/actionEditor/midiActionEditor.h"
@@ -38,14 +37,16 @@ namespace giada {
 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);
 }
 
 
@@ -80,9 +81,9 @@ void geNoteEditor::scroll()
 /* -------------------------------------------------------------------------- */
 
 
-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
index be900d399bc998a7c68492d1e6b8474cc764f8e0..143dd835efecafe045a4c527308973ffe23b89b4 100644 (file)
@@ -37,8 +37,6 @@ namespace v
 {
 class gdMidiActionEditor;
 class gePianoRoll;
-
-
 class geNoteEditor : public geScroll
 {
 public:
@@ -46,7 +44,7 @@ public:
        geNoteEditor(Pixel x, Pixel y, gdMidiActionEditor* base);
        ~geNoteEditor();
 
-       void rebuild();
+       void rebuild(c::actionEditor::Data& d);
        void scroll();
 
        gePianoRoll* pianoRoll;
index 4ff9a552d68fec0e39794d958f700c5dcdd79942..08c4dbac29880f5bc62c5368eac7595f99b41f69 100644 (file)
@@ -27,7 +27,6 @@
 
 #include <cassert>
 #include <FL/Fl.H>
-#include "core/channels/midiChannel.h"
 #include "core/conf.h"
 #include "core/const.h"
 #include "core/clock.h"
@@ -36,6 +35,7 @@
 #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"
@@ -46,9 +46,9 @@
 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);
 }
@@ -72,7 +72,7 @@ void gePianoRoll::drawSurface1()
 
        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 */
 
@@ -179,11 +179,12 @@ void gePianoRoll::draw()
 {
        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
 
@@ -216,7 +217,7 @@ void gePianoRoll::onAddAction()
 {
        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
@@ -228,7 +229,7 @@ void gePianoRoll::onAddAction()
 
 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
 }
@@ -314,7 +315,7 @@ void gePianoRoll::onRefreshAction()
        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
 }
@@ -355,9 +356,9 @@ Pixel gePianoRoll::getPianoItemW(Pixel px, const m::Action& a1, const m::Action&
 /* -------------------------------------------------------------------------- */
 
 
-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. */
@@ -365,7 +366,7 @@ void gePianoRoll::rebuild()
        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;
index 080ce937fd0dfb778e8ff5592e1ad33e59ace3f6..3f49c2c69f2274fb38b57f0928d6f771aa697ecb 100644 (file)
@@ -50,12 +50,12 @@ public:
        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;
 
index a45cb47f22ff8c4d94cf87285d15b9db59809a59..ba42a88fab77e8ed9f48acfeed1fdd0aef4a9735 100644 (file)
@@ -26,7 +26,6 @@
 
 
 #include <FL/fl_draw.H>
-#include "core/channels/sampleChannel.h"
 #include "core/const.h"
 #include "core/action.h"
 #include "sampleAction.h"
index c54fc00df04e00cbfe35ddac48d824701b03af43..24bc9dfacac8bacad1dcc6efe284036efacf91a4 100644 (file)
 #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"
@@ -44,8 +43,8 @@
 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)
 {
 }
 
@@ -62,20 +61,12 @@ geSampleActionEditor::~geSampleActionEditor()
 /* -------------------------------------------------------------------------- */
 
 
-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. */
@@ -83,12 +74,12 @@ void geSampleActionEditor::rebuild()
        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;
@@ -140,9 +131,7 @@ void geSampleActionEditor::draw()
 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);
 }
 
 
@@ -151,9 +140,7 @@ void geSampleActionEditor::onAddAction()
 
 void geSampleActionEditor::onDeleteAction()  
 {
-       c::actionEditor::deleteSampleAction(m_base->channelId, m_action->a1);
-       
-       m_base->rebuild();
+       c::actionEditor::deleteSampleAction(m_data->channelId, m_action->a1);
 }
 
 
@@ -218,7 +205,7 @@ void geSampleActionEditor::onRefreshAction()
                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();
 }
@@ -229,12 +216,8 @@ void geSampleActionEditor::onRefreshAction()
 
 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::
index 05bf0b80270be0554198539b959dd74582eac240..1d23d3d1edfbaeea677fc1c9b014c38d1023b2bb 100644 (file)
@@ -46,12 +46,12 @@ class geSampleActionEditor : public geBaseActionEditor
 {
 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:
 
index c3d5195f0b16e9e12e5d2b61e0d9906265afa770..57918422778f7b0e5d4602f9067155c6404aa358 100644 (file)
@@ -30,7 +30,6 @@
 #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"
@@ -44,8 +43,8 @@
 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)
 {
 }
 
@@ -110,9 +109,9 @@ int geVelocityEditor::yToValue(Pixel px) const
 /* -------------------------------------------------------------------------- */
 
 
-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. */
@@ -120,8 +119,8 @@ void geVelocityEditor::rebuild()
        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;
 
index 3dc141ad510aa7324c7bd0bbba1a701ba31e4016..be19b8ad924994afa2a448e26a6a41c0565cdeb6 100644 (file)
@@ -46,20 +46,20 @@ class geVelocityEditor : public geBaseActionEditor
 {
 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;
diff --git a/src/gui/elems/basics/baseButton.cpp b/src/gui/elems/basics/baseButton.cpp
deleted file mode 100644 (file)
index eb82d80..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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();
-}
diff --git a/src/gui/elems/basics/baseButton.h b/src/gui/elems/basics/baseButton.h
deleted file mode 100644 (file)
index 6c60eb1..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
index 8b4817b4591ca0559e4d92bb91d553d7e5ae3ca6..14e23150cc931bd9599aabaae5e89c1d6d4e6b63 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#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 charl, 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
index 81420dedad8726764c833680f73dda93c46f8b00..bfd52763ef76dd21c4bdb57009031c4147f9f12f 100644 (file)
@@ -36,7 +36,9 @@ class geBox : public Fl_Box
 {
 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;
 };
 
 
index 979c3db06801bcc0c617526056243726a79dd297..57787ccfa4c101cafe1b5e20c932bd82a44ed68a 100644 (file)
@@ -41,7 +41,7 @@ void g_customBorderBox(int x, int y, int w, int h, Fl_Color c)
 }
 
 
-void g_customUpBox(int x, int y, int w, int h, Fl_Color c)
+void g_customUpBox(int x, int y, int w, int h, Fl_Color /*c*/)
 {
   fl_color(G_COLOR_GREY_2);
   fl_rectf(x, y, w, h);
index a13134e2d49d1389f189b1d88d2e8d8de34d2d32..9df68827e4412749dc36e71eb85f00f935095db8 100644 (file)
@@ -34,9 +34,9 @@
 #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);
index 9df3053c3a86df3f60dfa92014a0e3648e6f4f47..66b8539c5df776a952722d8c20a61ecd89a04952 100644 (file)
 
 #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)
 {
 }
 
@@ -49,7 +50,7 @@ geButton::geButton(int x, int y, int w, int h, const char* l,
 
 void geButton::draw()
 {
-       geBaseButton::draw();
+       //Fl_Button::draw();
 
        if (active())
                if (value()) draw(imgOn,  bgColor1, txtColor);
@@ -68,11 +69,14 @@ void geButton::draw(const char** img, Fl_Color bgColor, Fl_Color textColor)
 
        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
index c62541ae08f16b2db655392f0cfd2f6073635cbe..81cc9d7355686604afb3bec44efdb1c4017b8ba5 100644 (file)
 #define GE_BUTTON_H
 
 
-#include "baseButton.h"
+#include <FL/Fl_Button.H>
 
 
-class geButton : public geBaseButton
+class geButton : public Fl_Button
 {
 public:
 
index 9a47de92198c99b89c4c0336e73b9dec7cc4f04d..96c2cf578a6c24867281edff57c1edcf180204c8 100644 (file)
@@ -25,6 +25,7 @@
  * -------------------------------------------------------------------------- */
 
 
+#include <cstring>
 #include <FL/fl_draw.H>
 #include "core/const.h"
 #include "check.h"
@@ -41,19 +42,28 @@ geCheck::geCheck(int x, int y, int w, int h, const char* l)
 
 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;
+}
+
index 3f7d8061d5d2d4a2abbb6f5733e440b7b02405d2..dbe53408aca244be6c75dfe5a8473e3039e07ea1 100644 (file)
@@ -39,6 +39,10 @@ public:
        geCheck(int x, int y, int w, int h, const char *l=0);
 
        void draw() override;
+
+private:
+
+       bool hasMultilineText() const;
 };
 
 
index 4c3a3f940b4d8be79bd5f3527202d3a5a7d5fd21..91b62cb2ea58c7762cfc1bd196afb2245cefc992 100644 (file)
 
 #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());
 }
 
 
@@ -48,38 +69,50 @@ geChoice::geChoice(int x, int y, int w, int h, const char *l, bool ang)
 
 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
index 8c008c67863e7b8f7f63c8537e95648c7edfbacb..40a41c8b9ca6433b35ac3830cdce92765a777659 100644 (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
diff --git a/src/gui/elems/basics/group.cpp b/src/gui/elems/basics/group.cpp
new file mode 100644 (file)
index 0000000..d888c1f
--- /dev/null
@@ -0,0 +1,94 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gui/elems/basics/group.h b/src/gui/elems/basics/group.h
new file mode 100644 (file)
index 0000000..3bc0ae6
--- /dev/null
@@ -0,0 +1,76 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gui/elems/basics/pack.cpp b/src/gui/elems/basics/pack.cpp
new file mode 100644 (file)
index 0000000..288df56
--- /dev/null
@@ -0,0 +1,62 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gui/elems/basics/pack.h b/src/gui/elems/basics/pack.h
new file mode 100644 (file)
index 0000000..dda1233
--- /dev/null
@@ -0,0 +1,67 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
index fb405113351bed7ec812284366108b39bd830bb9..eb1fce098c61a82e0203063c0ea96189e2f88882 100644 (file)
@@ -35,8 +35,9 @@
 
 
 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);
diff --git a/src/gui/elems/basics/scrollPack.cpp b/src/gui/elems/basics/scrollPack.cpp
new file mode 100644 (file)
index 0000000..364cbff
--- /dev/null
@@ -0,0 +1,89 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gui/elems/basics/scrollPack.h b/src/gui/elems/basics/scrollPack.h
new file mode 100644 (file)
index 0000000..64de3b6
--- /dev/null
@@ -0,0 +1,73 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
index be6110182d2389e1685eb096b6a9d9026445d90e..659e896322d8632499063f4d6cd737bcfac69366 100644 (file)
@@ -138,7 +138,7 @@ int geBrowser::handle(int e)
 
 std::string geBrowser::getCurrentDir()
 {
-       return normalize(u::string::getRealPath(m_currentDir));
+       return normalize(u::fs::getRealPath(m_currentDir));
 }
 
 
@@ -158,7 +158,7 @@ std::string geBrowser::getSelectedItem(bool fullPath)
 #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()))));
        }
 }
 
index 88216b02da7ff6008b4e94436eda79b045e619de..b23924972bd3e2a1540deab6399f88b4d3cd2ba3 100644 (file)
@@ -49,7 +49,7 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H)
        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");
@@ -138,7 +138,7 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H)
                        break;
        }
 
-#elif defined (__APPLE__)
+#elif defined(__APPLE__)
 
        if (m::kernelAudio::hasAPI(RtAudio::MACOSX_CORE))
                soundsys->add("CoreAudio");
@@ -166,7 +166,7 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H)
 
        if (m::conf::conf.soundSystem != G_SYS_API_NONE) {
                fetchSoundDevs();
-               fetchOutChans(sounddevOut->value());
+               fetchOutChans();
                fetchInChans(sounddevIn->value());
 
                /* fill frequency dropdown menu */
@@ -200,7 +200,7 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H)
        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)");
@@ -218,11 +218,11 @@ geTabAudio::geTabAudio(int X, int Y, int W, int H)
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -240,7 +240,7 @@ void geTabAudio::cb_fetchInChans()
 
 void geTabAudio::cb_fetchOutChans()
 {
-       fetchOutChans(sounddevOut->value());
+       fetchOutChans();
        channelsOut->value(0);
 }
 
@@ -283,7 +283,7 @@ void geTabAudio::cb_deactivate_sounddev()
 
                /* the '?' button is added by fetchSoundDevs */
 
-               fetchOutChans(sounddevOut->value());
+               fetchOutChans();
                sounddevOut->activate();
                channelsOut->activate();
 
@@ -339,18 +339,25 @@ void geTabAudio::fetchInChans(int menuItem)
                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();
 
@@ -496,7 +503,7 @@ void geTabAudio::save()
        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;
@@ -505,20 +512,23 @@ void geTabAudio::save()
 
        /* 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;
index c84b8201f914e9d50319e4f8c411514c93b3b227..fdc53832589206930f736da2a3c9273199f32a09 100644 (file)
@@ -32,7 +32,6 @@
 #include <FL/Fl_Group.H>
 
 
-class geChoice;
 class geCheck;
 class geButton;
 class geInput;
@@ -41,6 +40,7 @@ class geInput;
 namespace giada {
 namespace v
 {
+class geChoice;
 class geTabAudio : public Fl_Group
 {
 public:
@@ -64,11 +64,11 @@ 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();
@@ -77,7 +77,7 @@ private:
 
        void fetchSoundDevs();
        void fetchInChans(int menuItem);
-       void fetchOutChans(int menuItem);
+       void fetchOutChans();
        int  findMenuDevice(geChoice* m, int device);
 
        int soundsysInitValue;
index 931e76148f654a41f03791255aa02dcc141789d0..c14437cdf762f39eeae3d05d3c6cbb8b3a0a6d44 100644 (file)
@@ -29,7 +29,6 @@
 #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);
 }
 
 
@@ -97,9 +69,9 @@ void geTabBehaviors::cb_radio_mutex(Fl_Widget* w)
 
 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
index f5a9f7c187414a4e108664bf89f498ba000db147..84f73cf143980c507dfd088adb63093e8508ea0c 100644 (file)
 
 
 #include <FL/Fl_Group.H>
-
-
-class geRadio;
-class geCheck;
+#include "gui/elems/basics/pack.h"
+#include "gui/elems/basics/check.h"
 
 
 namespace giada {
@@ -47,18 +45,13 @@ public:
 
        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::
 
index 4bfe54fc9194ffd4aac91a779c02db37e2195345..ff393e558741dc02a012fc812352be273be5dbab 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
+#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"
@@ -220,7 +216,7 @@ void geTabMidi::fetchSystems()
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
index 6b967577c66f70ab4efb8b038359dc10da6405ca..3b1cccfc49418c7754770fa20c9b25db0595ba6e 100644 (file)
 #include <FL/Fl_Group.H>
 
 
-class geChoice;
 class geCheck;
 
 
 namespace giada {
 namespace v
 {
+class geChoice;
 class geTabMidi : public Fl_Group
 {
 public:
@@ -60,7 +60,7 @@ private:
        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;
index c733679fe3e7e4afcfadc602917118ba43fc897c..4c119ee79ba9afdd255fa50ffb6f591c1d23a504 100644 (file)
 #include <FL/Fl_Group.H>
 
 
-class geChoice;
-
-
 namespace giada {
 namespace v
 {
+class geChoice;
 class geTabMisc : public Fl_Group
 {
 public:
index c49a1df84197180162c0e3d630aa1acb264bc79b..453fbe6d55d723a8b1e98ebc7eb746b3e9f62196 100644 (file)
@@ -33,7 +33,7 @@
 #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"
@@ -42,7 +42,6 @@
 #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"
@@ -60,8 +59,8 @@ geTabPlugins::geTabPlugins(int X, int Y, int W, int 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();
 
@@ -88,15 +87,15 @@ geTabPlugins::geTabPlugins(int X, int Y, int W, int H)
 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(); }
 
 
 /* -------------------------------------------------------------------------- */
index 7944df0a07c01c8858246f393b42ac998d7169c5..83318de2e9aaabafb0cfad7bb90ca17eff2b3ce1 100644 (file)
@@ -54,8 +54,8 @@ public:
 
 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();
 
index 1c9441f42d0f9d69eb80309a4e26488e60a48b9e..4247866c23f7a122027d8d256d8cfdfc495673db 100644 (file)
 
 #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"
@@ -51,9 +51,9 @@ extern giada::v::gdMainWindow* G_MainWin;
 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)
 {
 }
 
@@ -83,12 +83,12 @@ void geChannel::draw()
 /* -------------------------------------------------------------------------- */
 
 
-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
 
 
@@ -97,16 +97,18 @@ void geChannel::cb_openFxWindow(Fl_Widget* v, void* p) { ((geChannel*)p)->cb_ope
 
 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());
 }
 
 
@@ -115,7 +117,7 @@ void geChannel::refresh()
 
 void geChannel::cb_arm()
 {
-       c::channel::setArm(channelId, arm->value());
+       c::events::toggleArmChannel(m_channel.id, Thread::MAIN);
 }
 
 
@@ -124,7 +126,7 @@ void geChannel::cb_arm()
 
 void geChannel::cb_mute()
 {
-       c::channel::toggleMute(channelId);
+       c::events::toggleMuteChannel(m_channel.id, Thread::MAIN);
 }
 
 
@@ -133,7 +135,7 @@ void geChannel::cb_mute()
 
 void geChannel::cb_solo()
 {
-       c::channel::toggleSolo(channelId);
+       c::events::toggleSoloChannel(m_channel.id, Thread::MAIN);
 }
 
 
@@ -142,7 +144,7 @@ void geChannel::cb_solo()
 
 void geChannel::cb_changeVol()
 {
-       c::channel::setVolume(channelId, vol->value());
+       c::events::setChannelVolume(m_channel.id, vol->value(), Thread::MAIN);
 }
 
 
@@ -152,7 +154,7 @@ void geChannel::cb_changeVol()
 #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
 
@@ -218,10 +220,7 @@ void geChannel::packWidgets()
 
 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
@@ -234,7 +233,16 @@ bool geChannel::handleKey(int e)
                playButton->value(0);
                return true;
        }
-       
+
        return false;
 }
+
+
+/* -------------------------------------------------------------------------- */
+
+
+const c::channel::Data& geChannel::getData() const
+{
+       return m_channel;
+}
 }} // giada::v::
index 10180d8f8b9ce73dca078f2d06b9c448e38c7279..6b14e4c5e8d29f4cb14d4464672de063673d5c22 100644 (file)
@@ -30,6 +30,7 @@
 
 
 #include <FL/Fl_Group.H>
+#include "glue/channel.h"
 #include "core/types.h"
 
 
@@ -40,19 +41,14 @@ class geStatusButton;
 
 
 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;
 
@@ -72,7 +68,10 @@ public:
 
        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;
@@ -103,19 +102,19 @@ protected:
 
        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
@@ -127,6 +126,11 @@ protected:
        Spread widgets across available space. */
 
        void packWidgets();
+
+       /* m_channel
+       Channel's data. */
+
+       c::channel::Data m_channel;
 };
 }} // giada::v::
 
index eaa2057939097e5b56db80480e08b7c58fb2f8f8..c8e7f255ffa70f2db76baa20e8bc2094659bbda9 100644 (file)
@@ -30,6 +30,7 @@
 #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)
 {
 }
 
@@ -50,35 +50,21 @@ geChannelButton::geChannelButton(int x, int y, int w, int h, ID channelId)
 
 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;
+       }
 }
 
 
@@ -89,7 +75,7 @@ void geChannelButton::draw()
 {
        geButton::draw();
 
-       if (m_key == "")
+       if (m_channel.key == 0)
                return;
 
        /* draw background */
@@ -100,7 +86,7 @@ void geChannelButton::draw()
 
        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);
 }
 
 
@@ -154,5 +140,4 @@ void geChannelButton::setEndingMode()
 {
        bgColor0 = G_COLOR_GREY_4;
 }
-
 }} // giada::v::
index 633addb137bb441c572a2d48d1106d320aca66b9..a648d4ed8449735caa736601c4ec7090426d5440 100644 (file)
 
 
 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);
@@ -58,8 +58,7 @@ public:
 
 protected:
 
-       ID          m_channelId;
-       std::string m_key;
+    const c::channel::Data& m_channel;
 };
 }} // giada::v::
 
index b4dbb0b8d58b176b6ed6ecbf85bfb247c42e9654..4422e4fc26416577d00b63bf46f5d01f190cf4b4 100644 (file)
@@ -30,7 +30,8 @@
 #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));
 }
 
 
@@ -69,32 +72,29 @@ void geChannelMode::draw()
 {
        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;
        }
@@ -104,7 +104,7 @@ void geChannelMode::draw()
 /* -------------------------------------------------------------------------- */
 
 
-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); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -112,6 +112,6 @@ void geChannelMode::cb_changeMode(Fl_Widget* v, void* p) { ((geChannelMode*)v)->
 
 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::
index b6072b86d4b98fe8dbd0bbfffd2b0bdc942642f5..a3d2534c6050e1fa7e9d780fbb6b97f2e5af3878 100644 (file)
 
 
 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::
 
index 95d32fbeee075dcd6f99249c64de284a19bee5a4..616708a4a3063f071925c158cfb51a027e336b05 100644 (file)
 
 
 #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)
 {
 }
 
@@ -53,38 +49,32 @@ void geChannelStatus::draw()
        fl_rect(x(), y(), w(), h(), G_COLOR_GREY_4);              // reset border
        fl_rectf(x()+1, y()+1, w()-2, h()-2, G_COLOR_GREY_2);     // reset background
 
-       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::
index 3afbe8e655e1308bcff3d4b7a4cf83a3bd750062..90e0dbb13843abe3dcf4df9b60741e9817f50558 100644 (file)
 
 
 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::
 
index 1a2959fe671902569a79abd2c38107b249350067..85a41d25a3d4b28988e66fb271cd801b050937e7 100644 (file)
@@ -28,8 +28,7 @@
 #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"
@@ -70,38 +69,40 @@ void geColumn::refresh()
 /* -------------------------------------------------------------------------- */
 
 
-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);
@@ -164,7 +165,7 @@ void geColumn::cb_addChannel()
 geChannel* geColumn::getChannel(ID channelId) const
 {
        for (geChannel* c : m_channels)
-               if (c->channelId == channelId)
+               if (c->getData().id == channelId)
                        return c;
        return nullptr;
 }
@@ -214,17 +215,4 @@ int geColumn::computeHeight() const
                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::
index 745f3efca4d53b7c086fa8d8d406b69b565b4b9d..0ca3fc35013d1b1eb84bcd2bb6a8324ae8809149 100644 (file)
@@ -32,6 +32,7 @@
 #include <functional>
 #include <vector>
 #include <FL/Fl_Group.H>
+#include "glue/channel.h"
 #include "core/types.h"
 
 
@@ -55,7 +56,7 @@ public:
        /* 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. */
@@ -72,12 +73,11 @@ public:
 
 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;
 
index 01fc0e451e2b22c93063cd0a6fdb9821f2238004..26a9cde3905b89dae8e1404ad75bdb5b22b54078 100644 (file)
@@ -27,8 +27,6 @@
 
 #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"
@@ -49,22 +47,10 @@ namespace giada {
 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();
 }
 
@@ -102,13 +88,8 @@ void geKeyboard::rebuild()
        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();
 }
@@ -141,7 +122,7 @@ void geKeyboard::deleteAllColumns()
 /* -------------------------------------------------------------------------- */
 
 
-void geKeyboard::cb_addColumn(Fl_Widget* v, void* p)
+void geKeyboard::cb_addColumn(Fl_Widget* /*w*/, void* p)
 {
        ((geKeyboard*)p)->cb_addColumn();
 }
@@ -246,7 +227,7 @@ void geKeyboard::addColumn(int width, ID id)
 
        /* 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();
        };
index aa4feadd2feb8050742427f26fc61bdb421da88f..a1d3219b4bc2db2edb5c8b892aeaddd30ec6bf4d 100644 (file)
@@ -30,8 +30,8 @@
 
 
 #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"
 
@@ -45,9 +45,7 @@ namespace v
 {
 class geColumn;
 class geChannel;
-class geSampleChannel;
-
-class geKeyboard : public Fl_Scroll
+class geKeyboard : public geScroll
 {
 public:
 
@@ -104,7 +102,7 @@ private:
 
        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);
index c70390718b3bcc09b58de9d37e0e2d50a8835ce0..d2148ce5179ca563f4b1f02004fa0275f5be2296 100644 (file)
@@ -29,7 +29,6 @@
 #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"
@@ -83,38 +82,37 @@ enum class Menu
 
 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;
        }
 }
@@ -124,8 +122,9 @@ void menuCallback(Fl_Widget* w, void* v)
 /* -------------------------------------------------------------------------- */
 
 
-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);
@@ -135,7 +134,7 @@ geMidiChannel::geMidiChannel(int X, int Y, int W, int H, ID channelId)
 
        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)
@@ -149,18 +148,15 @@ geMidiChannel::geMidiChannel(int X, int Y, int W, int H, ID channelId)
 
        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
@@ -173,10 +169,9 @@ geMidiChannel::geMidiChannel(int X, int Y, int W, int H, ID channelId)
        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
@@ -186,8 +181,8 @@ geMidiChannel::geMidiChannel(int X, int Y, int W, int H, ID channelId)
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -195,7 +190,7 @@ void geMidiChannel::cb_openMenu(Fl_Widget* v, void* p) { ((geMidiChannel*)p)->cb
 
 void geMidiChannel::cb_playButton()
 {
-       v::dispatcher::dispatchTouch(this, playButton->value());
+       v::dispatcher::dispatchTouch(*this, playButton->value());
 }
 
 
@@ -207,7 +202,7 @@ void geMidiChannel::cb_openMenu()
        Fl_Menu_Item rclick_menu[] = {
                {"Edit actions...", 0, menuCallback, (void*) Menu::EDIT_ACTIONS},
                {"Clear actions",   0, menuCallback, (void*) Menu::CLEAR_ACTIONS, FL_SUBMENU},
-                       {"All",           0, menuCallback, (void*) Menu::CLEAR_ACTIONS_ALL},
+                       {"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},
@@ -220,11 +215,8 @@ void geMidiChannel::cb_openMenu()
 
        /* 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);
index 0dd051d4b0ac4aa78ba0a7600ec180792e579352..4a12c8b00c386b01f12530dfbe1934e7627b7555 100644 (file)
 
 
 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::
 
index b7c4dc1ecce4aec658471fb8c5c4c1db56f09826..41372023d16a7e7564322f4b8ca01e62dd22c0a0 100644 (file)
 
 
 #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());
 }
 
 
@@ -62,12 +46,25 @@ void geMidiChannelButton::refresh()
 {
        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::
index 712bfc7993ea633feab451a9198533f88fd23af5..45c702afbce56cd35cdd9843070ac42e2e7845e4 100644 (file)
 
 
 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::
 
index 2bfe9163bad2f35c2adf3640142730f0368f4950..3db66bea9e29ea08eeb78e55cc7a2c5c5a491556 100644 (file)
@@ -26,7 +26,8 @@
 
 
 #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"
@@ -37,6 +38,7 @@
 #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"
@@ -74,6 +76,7 @@ namespace
 enum class Menu
 {
        INPUT_MONITOR = 0,
+       OVERDUB_PROTECTION,
        LOAD_SAMPLE,
        EXPORT_SAMPLE,
        SETUP_KEYBOARD_INPUT,
@@ -98,57 +101,52 @@ enum class Menu
 
 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;
                }
@@ -156,32 +154,32 @@ void menuCallback(Fl_Widget* w, void* v)
                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;
                }
        }
@@ -194,8 +192,8 @@ void menuCallback(Fl_Widget* w, void* v)
 /* -------------------------------------------------------------------------- */
 
 
-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);
@@ -204,11 +202,11 @@ geSampleChannel::geSampleChannel(int X, int Y, int W, int H, ID channelId)
 #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)
@@ -222,21 +220,15 @@ geSampleChannel::geSampleChannel(int X, int Y, int W, int H, ID channelId)
 
        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
@@ -249,13 +241,11 @@ geSampleChannel::geSampleChannel(int X, int Y, int W, int H, ID channelId)
        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
@@ -265,9 +255,9 @@ geSampleChannel::geSampleChannel(int X, int Y, int W, int H, ID channelId)
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -275,7 +265,7 @@ void geSampleChannel::cb_readActions(Fl_Widget* v, void* p) { ((geSampleChannel*
 
 void geSampleChannel::cb_playButton()
 {
-       v::dispatcher::dispatchTouch(this, playButton->value());
+       v::dispatcher::dispatchTouch(*this, playButton->value());
 }
 
 
@@ -284,19 +274,6 @@ void geSampleChannel::cb_playButton()
 
 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. */
 
@@ -305,7 +282,9 @@ void geSampleChannel::cb_openMenu()
 
        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},
@@ -325,21 +304,20 @@ void geSampleChannel::cb_openMenu()
                {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);
@@ -360,7 +338,10 @@ void geSampleChannel::cb_openMenu()
 
 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);
 }
 
 
@@ -371,17 +352,20 @@ void geSampleChannel::refresh()
 {
        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();
 }
 
 
index 8e42f809e07850da2fa3324e7d601197ba9b8ff1..c0d7db382c1bcb7079e641e08670f1816dd262fe 100644 (file)
@@ -29,6 +29,7 @@
 #define GE_SAMPLE_CHANNEL_H
 
 
+#include "glue/channel.h"
 #include "channel.h"
 
 
@@ -36,23 +37,19 @@ class geStatusButton;
 
 
 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;
@@ -60,9 +57,9 @@ public:
 
 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();
index 74aa1e5255c3e9a237b5447172f7d07783d7ee9f..d7d08cc3c6c683e8f5bcff74c7a9b901a5ef4708 100644 (file)
 
 
 #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"
@@ -48,33 +41,18 @@ extern giada::v::gdMainWindow* G_MainWin;
 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;
+       }
 }
 
 
@@ -84,15 +62,12 @@ geSampleChannelButton::geSampleChannelButton(int x, int y, int w, int h, ID chan
 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();
 }
@@ -112,7 +87,7 @@ int geSampleChannelButton::handle(int e)
                        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;
                }
index e59cc08e7918fc51e6a983b2e917d8df90a2d9bd..01b36e723f4796decbcd01f5db354a7a37982738 100644 (file)
 
 
 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;
 
index d779a4d3ded9567380fdf6ad57dbdf8a53004af9..6e44030852ea86bac14f498a4cdd86e2fe5ba43d 100644 (file)
 
 
 #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"
@@ -48,49 +46,40 @@ namespace giada {
 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
 }
 
@@ -98,12 +87,12 @@ geMainIO::geMainIO(int x, int y)
 /* -------------------------------------------------------------------------- */
 
 
-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
 
 
@@ -112,19 +101,21 @@ void geMainIO::cb_inToOut    (Fl_Widget* v, void* p) { ((geMainIO*)p)->cb_inToOu
 
 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());
+}
+
 /* -------------------------------------------------------------------------- */
 
 
@@ -141,12 +132,6 @@ void geMainIO::cb_masterFxIn()
        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
 
 
@@ -155,13 +140,13 @@ void geMainIO::cb_inToOut()
 
 void geMainIO::setOutVol(float v)
 {
-  outVol->value(v);
+       outVol.value(v);
 }
 
 
 void geMainIO::setInVol(float v)
 {
-  inVol->value(v);
+       inVol.value(v);
 }
 
 
@@ -172,13 +157,13 @@ void geMainIO::setInVol(float v)
 
 void geMainIO::setMasterFxOutFull(bool v)
 {
-  masterFxOut->setStatus(v);
+       masterFxOut.setStatus(v);
 }
 
 
 void geMainIO::setMasterFxInFull(bool v)
 {
-  masterFxIn->setStatus(v);
+       masterFxIn.setStatus(v);
 }
 
 #endif
@@ -189,10 +174,10 @@ void geMainIO::setMasterFxInFull(bool v)
 
 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();
 }
 
 
@@ -201,20 +186,14 @@ void geMainIO::refresh()
 
 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::
index 479c86a231e96ae3e883c8b60dae1581f7f94bcc..f393498010f036dd363fda15c617bc49984db8d4 100644 (file)
 #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:
 
@@ -61,29 +60,29 @@ 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::
index 0f84ba0baee8c051012787bf6eb757f8cc87cc5b..dfdedf4ea00c289537c0e961eabe66f4896047af 100644 (file)
@@ -27,8 +27,6 @@
 
 #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"
@@ -58,29 +56,26 @@ namespace giada {
 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);
        });
 }
@@ -89,8 +84,8 @@ geMainMenu::geMainMenu(int x, int y)
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -135,7 +130,7 @@ void geMainMenu::cb_file()
        }
        else
        if (strcmp(m->label(), "Close project") == 0) {
-               c::main::resetToInitState(/*createColumns=*/true);
+               c::main::closeProject();
        }
 #ifndef NDEBUG
        else
@@ -156,7 +151,7 @@ void geMainMenu::cb_file()
 void geMainMenu::cb_edit()
 {
        Fl_Menu_Item menu[] = {
-               {"Clear all samples"},
+               {"Free all Sample channels"},
                {"Clear all actions"},
                {"Setup global MIDI input..."},
                {0}
@@ -177,7 +172,7 @@ void geMainMenu::cb_edit()
        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) 
index 39e436b5e9ed4924d15f717d676a6f88175c5c38..9983aabc2d6767b4ae29822b6d96c1a3c43f2811 100644 (file)
@@ -29,7 +29,7 @@
 #define GE_MAIN_MENU_H
 
 
-#include <FL/Fl_Pack.H>
+#include "gui/elems/basics/pack.h"
 
 
 class geButton;
@@ -38,7 +38,7 @@ class geButton;
 namespace giada {
 namespace v
 {
-class geMainMenu : public Fl_Pack
+class geMainMenu : public gePack
 {
 public:
 
@@ -46,8 +46,8 @@ 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();
 
index 0479c940daa8308d3b96b277598ec3bb0f1b3b01..644b50529831359ad24c8c92a19882079c7e99a6 100644 (file)
 
 
 #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"
@@ -50,57 +47,45 @@ namespace giada {
 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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -108,7 +93,7 @@ void geMainTimer::cb_divider   (Fl_Widget* v, void* p) { ((geMainTimer*)p)->cb_d
 
 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);
 }
 
 
@@ -126,7 +111,7 @@ void geMainTimer::cb_meter()
 
 void geMainTimer::cb_quantizer()
 {
-       c::main::quantize(quantizer->value());
+       c::main::quantize(m_quantizer.value());
 }
 
 
@@ -135,7 +120,7 @@ void geMainTimer::cb_quantizer()
 
 void geMainTimer::cb_multiplier()
 {
-       c::main::beatsMultiply();
+       c::events::multiplyBeats();
 }
 
 
@@ -144,7 +129,7 @@ void geMainTimer::cb_multiplier()
 
 void geMainTimer::cb_divider()
 {
-       c::main::beatsDivide();
+       c::events::divideBeats();
 }
 
 
@@ -153,24 +138,27 @@ void geMainTimer::cb_divider()
 
 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();   
        }
 }
 
@@ -180,12 +168,17 @@ void geMainTimer::refresh()
 
 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
 }
 
 
@@ -194,13 +187,13 @@ void geMainTimer::rebuild()
 
 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)
 }
 
 
@@ -210,16 +203,16 @@ void geMainTimer::setBpm(float v)
 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();
   }
 }
 
@@ -229,7 +222,7 @@ void geMainTimer::setLock(bool v)
 
 void geMainTimer::setQuantizer(int q)
 {
-       quantizer->value(q);
+       m_quantizer.value(q);
 }
 
 
@@ -239,7 +232,7 @@ void geMainTimer::setQuantizer(int 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::
index cd4b25861aee35d8b58276c15510c5a73c187524..06df513c2af229b5cff968562e8d09cb5de7d112 100644 (file)
 #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:
 
@@ -54,28 +57,30 @@ 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::
 
index 6e91586b7b49c34fd687c8615fcb51431e864242..3f1bdce93e52474c51e151f1fb0a1e8aa2db8dc5 100644 (file)
 #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"
@@ -44,45 +46,48 @@ namespace giada {
 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();
        });
 }
 
@@ -95,7 +100,7 @@ void geMainTransport::refresh()
        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::
index dcf7a74e42fba378acfdf08c087d21f6842dff6b..063f206a839f7a64b7fe5e0b469cbb5bc0d67470 100644 (file)
 #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:
 
@@ -51,11 +52,11 @@ private:
 
        geButton* rewind;
        geStatusButton* play;
-       
-       geButton* recTriggerMode;
+       geBox* spacer1; 
+       geStatusButton* recTriggerMode;
        geStatusButton* recAction;
        geStatusButton* recInput;
-
+       geBox* spacer2;
        geStatusButton* metronome;
 };
 }} // giada::v::
diff --git a/src/gui/elems/midiIO/midiLearner.cpp b/src/gui/elems/midiIO/midiLearner.cpp
new file mode 100644 (file)
index 0000000..17070a0
--- /dev/null
@@ -0,0 +1,133 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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::
diff --git a/src/gui/elems/midiIO/midiLearner.h b/src/gui/elems/midiIO/midiLearner.h
new file mode 100644 (file)
index 0000000..e2d6b3f
--- /dev/null
@@ -0,0 +1,87 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gui/elems/midiIO/midiLearnerBase.cpp b/src/gui/elems/midiIO/midiLearnerBase.cpp
deleted file mode 100644 (file)
index 1da5e87..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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::
diff --git a/src/gui/elems/midiIO/midiLearnerBase.h b/src/gui/elems/midiIO/midiLearnerBase.h
deleted file mode 100644 (file)
index e0f3e73..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
diff --git a/src/gui/elems/midiIO/midiLearnerChannel.cpp b/src/gui/elems/midiIO/midiLearnerChannel.cpp
deleted file mode 100644 (file)
index a7dd485..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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::
diff --git a/src/gui/elems/midiIO/midiLearnerChannel.h b/src/gui/elems/midiIO/midiLearnerChannel.h
deleted file mode 100644 (file)
index d22177a..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
diff --git a/src/gui/elems/midiIO/midiLearnerMaster.cpp b/src/gui/elems/midiIO/midiLearnerMaster.cpp
deleted file mode 100644 (file)
index cb4a86f..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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::
diff --git a/src/gui/elems/midiIO/midiLearnerMaster.h b/src/gui/elems/midiIO/midiLearnerMaster.h
deleted file mode 100644 (file)
index 8bb2829..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
diff --git a/src/gui/elems/midiIO/midiLearnerPack.cpp b/src/gui/elems/midiIO/midiLearnerPack.cpp
new file mode 100644 (file)
index 0000000..af8653c
--- /dev/null
@@ -0,0 +1,92 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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::
diff --git a/src/gui/elems/midiIO/midiLearnerPack.h b/src/gui/elems/midiIO/midiLearnerPack.h
new file mode 100644 (file)
index 0000000..228a06c
--- /dev/null
@@ -0,0 +1,61 @@
+/* -----------------------------------------------------------------------------
+ *
+ * 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
diff --git a/src/gui/elems/midiIO/midiLearnerPlugin.cpp b/src/gui/elems/midiIO/midiLearnerPlugin.cpp
deleted file mode 100644 (file)
index a44b153..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
diff --git a/src/gui/elems/midiIO/midiLearnerPlugin.h b/src/gui/elems/midiIO/midiLearnerPlugin.h
deleted file mode 100644 (file)
index 482cbaf..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/* -----------------------------------------------------------------------------
- *
- * 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
index 7ca6e94beb2519c96c145b35ffd0f5faa2a7e113..49ab33dea5eb697f1dc88d28377e7f80be9a2191 100644 (file)
 
 
 #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"
 
@@ -90,7 +90,7 @@ void gePluginBrowser::refresh()
                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());
        }
@@ -103,7 +103,7 @@ void gePluginBrowser::refresh()
 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());
@@ -114,7 +114,7 @@ void gePluginBrowser::computeWidths()
        }
        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;
 }
index 79489e33b870cad92b0c7ffc9889f2e739d4a0ae..b85e52b107916885364c2d44a846f41a5b63ff96 100644 (file)
 
 #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);
 }
 
 
@@ -109,19 +107,19 @@ gePluginElement::gePluginElement(ID pluginId, ID channelId, int X, int Y, int W)
 
 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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -131,7 +129,7 @@ void gePluginElement::cb_shiftUp()
 {
        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);
 }
 
 
@@ -142,7 +140,7 @@ void gePluginElement::cb_shiftDown()
 {
        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);
 }
 
 
@@ -155,8 +153,8 @@ void gePluginElement::cb_removePlugin()
        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);
 }
 
 
@@ -165,14 +163,10 @@ void gePluginElement::cb_removePlugin()
 
 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);
@@ -181,10 +175,10 @@ void gePluginElement::cb_openPluginWindow()
                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);
        }
@@ -196,7 +190,7 @@ void gePluginElement::cb_openPluginWindow()
 
 void gePluginElement::cb_setBypass()
 {
-       c::plugin::toggleBypass(m_pluginId);
+       c::plugin::toggleBypass(m_plugin.id);
 }
 
 
@@ -205,7 +199,7 @@ void gePluginElement::cb_setBypass()
 
 void gePluginElement::cb_setProgram()
 {
-       c::plugin::setProgram(m_pluginId, program->value());
+       c::plugin::setProgram(m_plugin.id, program.value());
 }
 }} // giada::v::
 
index b6ca89a467756c7c8a46343876db8a7bc69cc682..ed8b0dad3f36bf0170d1038ea1f9c2fdf2e1507d 100644 (file)
 #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();
@@ -73,8 +71,7 @@ private:
        void cb_shiftDown();
        void cb_setProgram();
 
-       ID m_channelId;
-       ID m_pluginId;
+       c::plugin::Plugin m_plugin;
 };
 }} // giada::v::
 
index c401a5966f3f8b4c7495291674bcf637517829b8..a3c42dd907b06fd8a4ec44ba6d502dea6b5fe5ad 100644 (file)
 #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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -84,7 +78,7 @@ void gePluginParameter::cb_setValue(Fl_Widget* v, void* p)  { ((gePluginParamete
 
 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);
 }
 
@@ -92,18 +86,11 @@ void gePluginParameter::cb_setValue()
 /* -------------------------------------------------------------------------- */
 
 
-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::
 
index e3b2aff7f389330601f81e6eb1bfbdadb6e24281..3934f19da2dac60e373acc1c80463873e871a19d 100644 (file)
@@ -41,24 +41,27 @@ class geSlider;
 
 
 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;
index 9cedcb51d7e4f6317a9799ef10d45580eb6aaffe..9b79e4b5b5cb1ed495b0369060f1f67b393ab0d9 100644 (file)
@@ -26,7 +26,6 @@
 
 
 #include <FL/Fl.H>
-#include "core/channels/sampleChannel.h"
 #include "core/const.h"
 #include "core/waveFx.h"  
 #include "glue/channel.h"
@@ -85,9 +84,9 @@ void geBoostTool::rebuild()
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 /* -------------------------------------------------------------------------- */
index 1ceb7f2d09e25a796ec6ee492e750a8b9ba50c98..790763c8ee644a82621a13a68eb62990e7bbfb61 100644 (file)
@@ -51,9 +51,9 @@ public:
 
 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();
index 6fb693473c1e4499b050bc2131165578e5a29bcb..bbb66a82bd10654218db742f0596ca159bd07046 100644 (file)
 
 
 #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());
        }
 }
 
@@ -101,8 +102,8 @@ void gePanTool::rebuild()
 /* -------------------------------------------------------------------------- */
 
 
-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(); }
 
 
 
@@ -111,7 +112,7 @@ void gePanTool::cb_panReset(Fl_Widget* w, void* p) { ((gePanTool*)p)->cb_panRese
 
 void gePanTool::cb_panning()
 {
-       c::channel::setPan(m_channelId, dial->value());
+       c::events::sendChannelPan(m_data->channelId, m_dial.value());
 }
 
 
@@ -120,7 +121,6 @@ void gePanTool::cb_panning()
 
 void gePanTool::cb_panReset()
 {
-       c::channel::setPan(m_channelId, 0.5f);
+       c::events::sendChannelPan(m_data->channelId, 0.5f);
 }
-
 }} // giada::v::
index 1adeca12e4e6428cabbacdc35f0c4507509ce605..609b5bb00b7df22e1f2196fc9afa141d9c1ad6ae 100644 (file)
 #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::
 
index fb7e5cf1f42803fc68b9ffdb09d468f57af13f88..756ef163338aaecf5b79beaa57d0308e53bf5497 100644 (file)
 
 
 #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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -112,7 +122,7 @@ void gePitchTool::cb_setPitchNum   (Fl_Widget* w, void* p) { ((gePitchTool*)p)->
 
 void gePitchTool::cb_setPitch()
 {
-       c::channel::setPitch(m_channelId, dial->value());
+       c::events::setChannelPitch(m_data->channelId, m_dial.value(), Thread::MAIN);
 }
 
 
@@ -121,7 +131,7 @@ void gePitchTool::cb_setPitch()
 
 void gePitchTool::cb_setPitchNum()
 {
-       c::channel::setPitch(m_channelId, atof(input->value()));
+       c::events::setChannelPitch(m_data->channelId, atof(m_input.value()), Thread::MAIN);     
 }
 
 
@@ -130,7 +140,7 @@ void gePitchTool::cb_setPitchNum()
 
 void gePitchTool::cb_setPitchHalf()
 {
-       c::channel::setPitch(m_channelId, dial->value()/2);
+       c::events::setChannelPitch(m_data->channelId, m_dial.value() / 2, Thread::MAIN);        
 }
 
 
@@ -139,7 +149,7 @@ void gePitchTool::cb_setPitchHalf()
 
 void gePitchTool::cb_setPitchDouble()
 {
-       c::channel::setPitch(m_channelId, dial->value()*2);
+       c::events::setChannelPitch(m_data->channelId, m_dial.value() * 2, Thread::MAIN);        
 }
 
 
@@ -148,13 +158,8 @@ void gePitchTool::cb_setPitchDouble()
 
 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);  
 }
 
 
@@ -163,13 +168,8 @@ void gePitchTool::cb_setPitchToBar()
 
 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);  
 }
 
 
@@ -178,7 +178,6 @@ void gePitchTool::cb_setPitchToSong()
 
 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::
index 10019c366c67199f25ec9fb33ebf2bb7e0b9ea8b..bc2ab586c3e8e7693f17a001b129217a86d383e1 100644 (file)
 #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();
@@ -66,16 +65,16 @@ private:
        void cb_resetPitch();
        void cb_setPitchNum();
 
-       ID m_channelId;
+       const c::sampleEditor::Data* m_data;
 
-       geBox*    label;
-       geDial*   dial;
-       geInput*  input;
-       geButtonpitchToBar;
-       geButtonpitchToSong;
-       geButtonpitchHalf;
-       geButtonpitchDouble;
-       geButtonpitchReset;
+       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::
 
index 6dbd341db02714187256739291a3a625c0ed7921..4c11c46b588a8443ddf14452d8844717fa68fe39 100644 (file)
@@ -27,7 +27,6 @@
 
 #include <cassert>
 #include <FL/Fl.H>
-#include "core/channels/sampleChannel.h"
 #include "core/model/model.h"
 #include "core/wave.h"
 #include "glue/channel.h"
@@ -35,9 +34,6 @@
 #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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -97,7 +99,7 @@ void geRangeTool::cb_resetStartEnd(Fl_Widget* w, void* p) { ((geRangeTool*)p)->c
 
 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()));
 }
 
 
@@ -106,13 +108,7 @@ void geRangeTool::cb_setChanPos()
 
 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::
index 02c2b2fb27784fcdef84f983f7ee41275e4b8967..65d07dc40bab8b7eb228b39fdc9ca04568fe1112 100644 (file)
 #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::
 
index 6b7cffb5f7436424bc3b26ff45da9f5c308de70a..6cbf480f72e35328a0413ceef5954b266bf001a8 100644 (file)
@@ -27,7 +27,6 @@
 
 #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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -78,7 +73,7 @@ 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()));
 }
 
 
@@ -94,12 +89,19 @@ void geShiftTool::cb_reset()
 /* -------------------------------------------------------------------------- */
 
 
-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());
 }
 
 
@@ -108,6 +110,6 @@ void geShiftTool::rebuild()
 
 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::
index aa26d065786034234c8c76dcf101be7f0a4944a2..dbd74f356b6c52c78ab3a0f78fc2d6d274b106d6 100644 (file)
 #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::
 
index 81ac99eeb710104487e515715113391757ed1b22..14416862a8a67a04f089d8413156764470f2cbc5 100644 (file)
 #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(); }
 
 
 /* -------------------------------------------------------------------------- */
@@ -95,7 +98,7 @@ void geVolumeTool::cb_setVolumeNum(Fl_Widget* w, void* p) { ((geVolumeTool*)p)->
 
 void geVolumeTool::cb_setVolume()
 {
-       c::channel::setVolume(m_channelId, dial->value(), false, true);
+       c::events::setChannelVolume(m_data->channelId, m_dial.value(), Thread::MAIN);
 }
 
 
@@ -104,8 +107,7 @@ void geVolumeTool::cb_setVolume()
 
 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::
index b719607819f470d89eb6a1eca9bfb9120a18b893..7ab23d97268fba9633d6988428d10913e15a1e82 100644 (file)
 #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::
 
index ceaf1e666117d6ed1797a4fe8d0399d807c63d60..f568281eeab06d433d713da95ff2cee0b01be963 100644 (file)
@@ -28,7 +28,6 @@
 #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"
@@ -68,9 +67,9 @@ void menuCallback_(Fl_Widget* w, void* v)
 {
        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();
@@ -89,32 +88,32 @@ void menuCallback_(Fl_Widget* w, void* v)
                        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}
 
 
 /* -------------------------------------------------------------------------- */
@@ -122,10 +121,9 @@ void menuCallback_(Fl_Widget* w, void* v)
 /* -------------------------------------------------------------------------- */
 
 
-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);
@@ -133,7 +131,7 @@ geWaveTools::geWaveTools(ID channelId, ID waveId, int x, int y, int w, int h)
        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);
 }
 
 
@@ -141,9 +139,10 @@ geWaveTools::geWaveTools(ID channelId, ID waveId, int x, int y, int w, int h)
 /* -------------------------------------------------------------------------- */
 
 
-void geWaveTools::rebuild()
+void geWaveTools::rebuild(const c::sampleEditor::Data& d)
 {
-       waveform->rebuild();
+       m_data = &d;
+       waveform->rebuild(d);
 }
 
 
@@ -152,11 +151,8 @@ void geWaveTools::rebuild()
 
 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();
 }
 
 
@@ -169,7 +165,7 @@ void geWaveTools::resize(int x, int y, int w, int h)
 
        if (this->w() == w || (this->w() != w && this->h() != h)) {   // vertical or both resize
                waveform->resize(x, y, waveform->w(), h-24);
-               waveform->rebuild();
+               waveform->rebuild(*m_data);
        }
 
        if (this->w() > waveform->w())
index ba8eb0ad402da6ac4fd0d4cf4f28aa774a6f9d6c..d920137bb65820fc2e40ed18a3779a373b915474 100644 (file)
@@ -40,7 +40,7 @@ class geWaveTools : public Fl_Scroll
 {
 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;
@@ -49,7 +49,7 @@ public:
        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
@@ -57,15 +57,16 @@ public:
        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::
 
index 1ad02a8580dafa0ad32e31396220afd53860120f..aec4f9ae167e9d190a009a4e627567d225efee43 100644 (file)
@@ -29,7 +29,6 @@
 #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;
@@ -75,9 +73,9 @@ geWaveform::geWaveform(ID channelId, ID waveId, int x, int y, int w, int h)
 
 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();
 }
 
@@ -87,9 +85,10 @@ void geWaveform::clearData()
 
 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;
 
@@ -100,16 +99,16 @@ int geWaveform::alloc(int datasize, bool force)
                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)
@@ -122,7 +121,7 @@ int geWaveform::alloc(int datasize, bool force)
        /* 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]. */
 
@@ -156,13 +155,13 @@ int geWaveform::alloc(int datasize, bool force)
                                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();
@@ -175,11 +174,8 @@ int geWaveform::alloc(int datasize, bool force)
 
 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;
 }
 
 
@@ -215,10 +211,10 @@ void geWaveform::drawWaveform(int from, int to)
 
        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]);
        }
 }
 
@@ -286,13 +282,7 @@ void geWaveform::drawStartEndPoints()
 
 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);
 }
@@ -303,8 +293,8 @@ void geWaveform::drawPlayHead()
 
 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
 
@@ -332,9 +322,10 @@ void geWaveform::draw()
 
 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());
@@ -346,7 +337,7 @@ int geWaveform::handle(int e)
                                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;
                }
 
@@ -378,7 +369,7 @@ int geWaveform::handle(int e)
 
                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. */
@@ -389,7 +380,7 @@ int geWaveform::handle(int e)
                        /* 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;
@@ -572,13 +563,10 @@ bool geWaveform::mouseOnSelectionB() const
 
 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;
 }
 
@@ -600,7 +588,7 @@ void geWaveform::fixSelection()
        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);
 }
 
 
@@ -619,10 +607,10 @@ void geWaveform::clearSelection()
 
 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. */
        
@@ -668,10 +656,11 @@ void geWaveform::stretchToWindow()
 /* -------------------------------------------------------------------------- */
 
 
-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();
 }
 
@@ -692,7 +681,7 @@ void geWaveform::setGridLevel(int l)
 {
        m_grid.points.clear();
        m_grid.level = l;
-       alloc(m_data.size, true); // force alloc
+       alloc(m_waveform.size, true); // force alloc
        redraw();
 }
 
@@ -711,7 +700,7 @@ bool geWaveform::isSelected() const
 
 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; }
 
 
 /* -------------------------------------------------------------------------- */
@@ -723,11 +712,8 @@ int geWaveform::getSelectionB() const { return m_selection.b; }
 
 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::
index d36691bb34e013631259943aa255c6fe7333f67f..82f311d497c1b59de1ab4b168b7196b6b3256453 100644 (file)
@@ -48,7 +48,7 @@ public:
 #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;
@@ -82,7 +82,7 @@ public:
        /* 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. */
@@ -99,7 +99,7 @@ public:
        /* 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:
 
@@ -125,7 +125,7 @@ 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
        {
@@ -188,8 +188,7 @@ private:
 
        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;
index 5db4c1ff45bab94ab8de18737d49a88e943ce0e3..bf698ee6bbcae614408ad9ec9c919348a7420723 100644 (file)
@@ -36,11 +36,11 @@ namespace giada {
 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);
index bee58ad9c3867f7f660419d38cddfcd697baca00..7bfcbe5bd4ed13354250750329addb0ecb5675c0 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
-#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
index 07b9499bbb164508e49ca266349c0b9069a15cd6..9eac2520aae9322a7ea260d905d5ddd1c9447f60 100644 (file)
@@ -27,6 +27,7 @@
  * -------------------------------------------------------------------------- */
 
 
+#include <filesystem>
 #if defined(_WIN32)                    // getcwd (unix) or __getcwd (win)
        #include <direct.h>
        #include <windows.h>
@@ -55,105 +56,45 @@ namespace giada {
 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();
 }
 
 
@@ -162,22 +103,16 @@ bool mkdir(const std::string &path)
 
 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();
 }
 
 
@@ -186,35 +121,16 @@ std::string dirname(const std::string& path)
 
 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();
 }
 
 
@@ -223,29 +139,26 @@ std::string getExt(const std::string& file)
 
 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;
@@ -257,31 +170,30 @@ std::string stripFileUrl(const std::string& f)
 
 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();
 }
 
 
@@ -290,18 +202,7 @@ std::string getHomePath()
 
 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;
 }
 
 
@@ -312,13 +213,12 @@ std::string getUpDir(const std::string& s)
 {
 #ifdef G_OS_WINDOWS
 
-       /* If root, let the user browse the drives list by returning "". */
+       // If root, let the user browse the drives list by returning "". 
        if (isRootDir(s))
                return "";
 
 #endif
 
-       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
index 39b92abce025052cf5579fcd707a7fb0ff2004bc..16fae9470437358e4156851edb9b75ee254bdf2c 100644 (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 */
 
index d207f5fd7a05c8e4f243bce990ce6d23f06130d6..72fa1b8623eb69d7f0536f6114f636815e84cd29 100644 (file)
 #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"
@@ -154,7 +153,7 @@ void updateStaticWidgets()
 
        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());
 }
 
 
@@ -269,6 +268,26 @@ std::string removeFltkChars(const std::string& s)
 /* -------------------------------------------------------------------------- */
 
 
+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);
index 9a05df539a9e6b9298675e9684418d3a7ad41b18..323b53412ccedb2d52a0ce737ddd172990bfd198 100644 (file)
@@ -30,6 +30,7 @@
 
 
 #include <string>
+#include "core/types.h"
 
 
 namespace giada
@@ -104,6 +105,11 @@ std::string removeFltkChars(const std::string& s);
 
 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);
 
index 2ea56b96219684f62f937bdf2ac3b07ad5886e5c..457d34f375e335c1d70e77845dc0757e33b10756 100644 (file)
 
 
 #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;
@@ -74,27 +56,6 @@ int init(int m)
 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
index ae11796c3f62adb5b51522f9d6ad90cecbc655d9..e7c43795c1cf3f8d87fa6f56484e9709651fb30f 100644 (file)
 #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. */
@@ -43,8 +53,46 @@ int init(int mode);
 
 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
index 50840211f5519a7cdc0d2b7f1d8d6fc7478647c8..612d0a41a77f345abf915c11add1a943eeddea4f 100644 (file)
@@ -29,6 +29,9 @@
 #define G_UTILS_MATH_H
 
 
+#include <type_traits>
+
+
 namespace giada {
 namespace u     {
 namespace math 
@@ -47,6 +50,9 @@ Maps 'x' in range [a, b] to a new range [w, z]. Source:
 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;
 }
 
@@ -57,31 +63,10 @@ Maps 'x' in range [0, b) to a new range [0, z]. */
 template <typename TI, typename TO>
 TO map(TI x, TI b, TO z)
 {
-       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::
 
index 7e22758c3e7bcfb58a036cbbc149d030fd6c1f92..2620d162ccc7cbb9db8366e6eea20db64c835b09 100644 (file)
@@ -30,7 +30,7 @@
 #include <iomanip>
 #include <cstdarg>
 #include <climits>
-#include "../core/const.h"
+#include "core/const.h"
 #include "string.h"
 
 
@@ -38,31 +38,6 @@ namespace giada {
 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)
@@ -89,7 +64,7 @@ std::string trim(const std::string& s)
 
 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();
@@ -109,7 +84,7 @@ std::string format(const char* format, ...)
        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. */
@@ -134,8 +109,8 @@ std::vector<std::string> split(std::string in, std::string sep)
        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);
index e100df1149e32fa73b00480c38791903eed1a2cc..d2ab13233e82ce3a1e62d640305938cc62ce0874 100644 (file)
@@ -51,8 +51,6 @@ std::string iToString(T t, bool hex=false)
        return out.str();       
 }
 
-std::string getRealPath(const std::string& path);
-
 std::string replace(std::string in, const std::string& search,
   const std::string& replace);
 
index 067b97d10bfcf4ec7022ee74afd918f424179b0d..a2b886049bc4e5df22bc4d7b3cc38a466a62cd02 100644 (file)
@@ -40,4 +40,4 @@ void sleep(int millisecs)
 {
        std::this_thread::sleep_for(std::chrono::milliseconds(millisecs));
 }
-}}};  // giada::u::time::
+}}}  // giada::u::time::
index c285068d7456e8c6e4e4967bceddcf0bda134b2f..b2f29c63ef1782ab1814ff0f753471807745a4c3 100644 (file)
@@ -36,7 +36,7 @@ namespace u     {
 namespace time 
 {
 void sleep(int millisecs);
-}}};
+}}}
 
 
 #endif
\ No newline at end of file
index 583d7de915a2581128cf626a88a5c81415f276ba..894d17e9ae60361d9dab4483270674d80389e901 100644 (file)
@@ -39,19 +39,12 @@ namespace u {
 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));
-}
-
-
 /* -------------------------------------------------------------------------- */
 
 
@@ -67,6 +60,6 @@ void remove(T& v, V val)
 {
     v.erase(std::remove(v.begin(), v.end(), val), v.end());
 }
-}}};  // giada::u::vector::
+}}}  // giada::u::vector::
 
 #endif
index 06019cbd99f6609c781c7c1cf03d525cdf150169..c60ba9b63daea1eda445011f8d7cb3cd25fe8ba9 100644 (file)
  * -------------------------------------------------------------------------- */
 
 
+#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"
@@ -72,4 +68,4 @@ std::string getRtMidiVersion()
        return RtMidi::getVersion();
 #endif
 }
-}}};  // giada::u::ver::
+}}}  // giada::u::ver::
index eb3eacfcf9fba01bbb5f9e998bda6167b4b27272..2640147f292c37a1cf3b7a158f947a3deada85e8 100644 (file)
@@ -39,7 +39,7 @@ namespace ver
 std::string getLibsndfileVersion();
 std::string getRtAudioVersion();
 std::string getRtMidiVersion();
-}}};  // giada::u::ver::
+}}}  // giada::u::ver::
 
 
 #endif
index b9d14993638aef3714cc4b167f30b84417c242e9..5ab5f1fafdaa32f92c748effbf1a8f50455a8aaa 100644 (file)
@@ -1,6 +1,6 @@
 #include <memory>
 #include "../src/core/audioBuffer.h"
-#include <catch.hpp>
+#include <catch2/catch.hpp>
 
 
 TEST_CASE("AudioBuffer")
@@ -32,14 +32,6 @@ 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);
@@ -87,13 +79,6 @@ TEST_CASE("AudioBuffer")
                        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;
        }
 }
index 32522b94e1aae9f026150d2e15cc0bb5c9f5618f..14ea358404630826a3c1053c8e9613c4c2b719eb 100644 (file)
@@ -1,6 +1,6 @@
 #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. */
index 6114c5aa800d8275dd1e52bd54b2ac307a3e803c..cb1cba6208ef17f41ac621787d1cd59dfe814aad 100644 (file)
@@ -1,6 +1,6 @@
 #include "../src/core/rcuList.h"
 #include "../src/core/types.h"
-#include <catch.hpp>
+#include <catch2/catch.hpp>
 
 
 using namespace giada;
index 8557a50e2921d8a6a6ad458e61a162922a208130..402efd7fb0cc7bf157abd29458876ab3fa60bf4d 100644 (file)
@@ -2,7 +2,7 @@
 #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")
diff --git a/tests/sampleChannel.cpp b/tests/sampleChannel.cpp
deleted file mode 100644 (file)
index dcb59f3..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-#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 */
-}
index 1cbc4b10207332d71a0fb353ecaaf6c66d3bb025..2ef9e4c9ae293d3692c1fc0ba335b6a773b8f89f 100644 (file)
@@ -1,51 +1,51 @@
 #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");
@@ -55,9 +55,9 @@ TEST_CASE("u::string")
 
 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));
 }
index 0d216333142928569099e725a3b0eee2a0d06d6f..535001a16a1747bc011ed29f108db3ea4e218481 100644 (file)
@@ -1,6 +1,6 @@
 #include <memory>
 #include "../src/core/wave.h"
-#include <catch.hpp>
+#include <catch2/catch.hpp>
 
 
 TEST_CASE("Wave")
index bc8bf21d0171a0b787d2b4384f3a70e5d7ae3717..456d6feeb3d1d38bd0a78612f163ceaa72974263 100644 (file)
@@ -4,7 +4,7 @@
 #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;
@@ -64,15 +64,6 @@ TEST_CASE("waveFx")
                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")
@@ -85,14 +76,6 @@ TEST_CASE("waveFx")
                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")
@@ -104,13 +87,6 @@ TEST_CASE("waveFx")
                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")
@@ -118,22 +94,13 @@ TEST_CASE("waveFx")
                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")
@@ -147,12 +114,5 @@ TEST_CASE("waveFx")
                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);          
-               }
        }
 }
index 790828b72f72bbc19e3d96c96da424868ee176a4..6414284e2ba7d176be5ec0b4371d5fd296ffa903 100644 (file)
@@ -1,8 +1,9 @@
 #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;
@@ -21,7 +22,8 @@ TEST_CASE("waveManager")
 
        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);
@@ -44,7 +46,8 @@ TEST_CASE("waveManager")
 
        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);